Android自定义View实现圆形切图效果

使用自定义View实现圆形ImageView的效果,具体内容如下

目前圆形边框还需要调整,这里有点问题

实现思路

使用一个Paint,将得到的Bitmap设置成paint的Shader,设置完成后,使用Matrix调整图片至居中,使用RectF约束边框,最后完成绘制

初始化Paint,设置Shader

private void init() {
  getBitmapFromDrawable();
  if (mBitmap == null) {
   return;
  }
  mShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

  // bitmap paint
  mFillPaint = new Paint();
  mFillPaint.setAntiAlias(true);
  mFillPaint.setStyle(Paint.Style.FILL);
  mFillPaint.setShader(mShader);

  // border paint
  mBoundPaint = new Paint();
  mBoundPaint.setAntiAlias(true);
  mBoundPaint.setStyle(Paint.Style.STROKE);
  mBoundPaint.setStrokeWidth(mBorderWidth);
  mBoundPaint.setColor(mBorderColor);

  // border rectF
  mBorderBound.set(calculateBitmapBound());

  // bitmap rectF
  mBitmapBound.set(calculateBitmapBound());
  mBitmapBound.inset(mBorderWidth - 10, mBorderWidth - 10);
  updateShaderMatrix();

 }

获取Drawable

private Bitmap getBitmapFromDrawable() {
  Drawable drawable = getDrawable();
  if (drawable instanceof BitmapDrawable) {
   mBitmap = ((BitmapDrawable) drawable).getBitmap();
   mBitmapWidth = mBitmap.getWidth();
   mBitmapHeight = mBitmap.getHeight();
   return mBitmap;
  }
  return null;
 }

计算边距

 /**
  * 计算Bitmap边距
  */
 private RectF calculateBitmapBound() {
  int availableWidth = getWidth() - getPaddingLeft() - getPaddingRight();
  int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();
  int sideLength = Math.min(availableWidth, availableHeight); // 可用的直径
  mRadius = sideLength / 2;

  int left = getPaddingLeft() + (availableWidth - sideLength) / 2;
  int top = getPaddingTop() + (availableHeight - sideLength) / 2;

  Log.d(TAG, "calculateBitmapBound: left >>> " + left + " top >>> " + top + " right >>> "
    + (left + sideLength) + " right >>> " + top + " bottom >>> " + (top + sideLength));
  return new RectF(left, top, left + sideLength, top + sideLength);
 }

调整Matrix,防止只显示图片边角

 /**
  * 调整图片缩放,目前只支持CenterCrop
  */
 private void updateShaderMatrix() {
  float scale;
  float dx = 0;
  float dy = 0;

  mShaderMatrix.set(null);

  // 调整缩放,使图片居中
  if (mBitmapWidth * mBitmapBound.height() > mBitmapBound.width() * mBitmapHeight) {
   scale = mBitmapBound.height() / (float) mBitmapHeight;
   dx = (mBitmapBound.width() - mBitmapWidth * scale) * 0.5f;
  } else {
   scale = mBitmapBound.width() / (float) mBitmapWidth;
   dy = (mBitmapBound.height() - mBitmapHeight * scale) * 0.5f;
  }

  Log.d(TAG, "updateShaderMatrix: scale >>> " + scale);
  mShaderMatrix.setScale(scale, scale);
  // TODO: 16-10-15 http://chroya.iteye.com/blog/713869
  // 回到中心点,便于下次缩放
  mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBitmapBound.left, (int) (dy + 0.5f) + mBitmapBound.top);

  mShader.setLocalMatrix(mShaderMatrix);
 }

onDraw

 @Override
 protected void onDraw(Canvas canvas) {
  if (mBitmap == null) {
   super.onDraw(canvas);
  }
  Log.d(TAG, "onDraw: centerX >>> " + mBitmapBound.centerX() + " centerY >>> " + mBitmapBound.centerY());
  canvas.drawCircle(mBitmapBound.centerX(), mBitmapBound.centerY(), mRadius, mFillPaint);
  // 绘制边框
  canvas.drawCircle(mBorderBound.centerX(), mBorderBound.centerY(), mRadius, mBoundPaint);
 }

完整代码

/**
 * Created by shixi_tianrui1 on 16-10-7.
 * 显示圆形图片的ImageView
 */

public class CircleImageView extends ImageView {

 private static final String TAG = "LOGGER";

 private BitmapShader mShader;
 private Paint mFillPaint; // 绘图
 private Paint mBoundPaint; // 绘制圆边

 private Bitmap mBitmap;
 private Drawable mDrawable;

 private int mBorderColor;  // 边框颜色
 private float mBorderWidth;  // 边框宽度

 private RectF mBorderBound = new RectF();
 private RectF mBitmapBound = new RectF();
 private Matrix mShaderMatrix = new Matrix();

 private int mRadius;
 private int mBitmapWidth;
 private int mBitmapHeight;

 private static final float DEFAULT_BORDER_WIDTH = 5;

 public CircleImageView(Context context) {
  this(context, null);
 }

 public CircleImageView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  TypedArray a = getResources().obtainAttributes(attrs, R.styleable.CircleImageView);
  mBorderColor = a.getColor(R.styleable.CircleImageView_borderColor, Color.BLUE);
  mBorderWidth = a.getDimension(R.styleable.CircleImageView_borderWidth, DEFAULT_BORDER_WIDTH);
  mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_borderWidth, 20);
  a.recycle();
 }

 private void init() {
  getBitmapFromDrawable();
  if (mBitmap == null) {
   return;
  }
  mShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

  // bitmap paint
  mFillPaint = new Paint();
  mFillPaint.setAntiAlias(true);
  mFillPaint.setStyle(Paint.Style.FILL);
  mFillPaint.setShader(mShader);

  // border paint
  mBoundPaint = new Paint();
  mBoundPaint.setAntiAlias(true);
  mBoundPaint.setStyle(Paint.Style.STROKE);
  mBoundPaint.setStrokeWidth(mBorderWidth);
  mBoundPaint.setColor(mBorderColor);

  // border rectF
  mBorderBound.set(calculateBitmapBound());

  // bitmap rectF
  mBitmapBound.set(calculateBitmapBound());
  mBitmapBound.inset(mBorderWidth - 10, mBorderWidth - 10);
  updateShaderMatrix();

 }

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  init();
 }

 /**
  * 计算Bitmap边距
  */
 private RectF calculateBitmapBound() {
  int availableWidth = getWidth() - getPaddingLeft() - getPaddingRight();
  int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();
  int sideLength = Math.min(availableWidth, availableHeight); // 可用的直径
  mRadius = sideLength / 2;

  int left = getPaddingLeft() + (availableWidth - sideLength) / 2;
  int top = getPaddingTop() + (availableHeight - sideLength) / 2;

  Log.d(TAG, "calculateBitmapBound: left >>> " + left + " top >>> " + top + " right >>> "
    + (left + sideLength) + " right >>> " + top + " bottom >>> " + (top + sideLength));
  return new RectF(left, top, left + sideLength, top + sideLength);
 }

 private Bitmap getBitmapFromDrawable() {
  Drawable drawable = getDrawable();
  if (drawable instanceof BitmapDrawable) {
   mBitmap = ((BitmapDrawable) drawable).getBitmap();
   mBitmapWidth = mBitmap.getWidth();
   mBitmapHeight = mBitmap.getHeight();
   return mBitmap;
  }
  return null;
 }

 @Override
 protected void onDraw(Canvas canvas) {
  if (mBitmap == null) {
   super.onDraw(canvas);
  }
  Log.d(TAG, "onDraw: centerX >>> " + mBitmapBound.centerX() + " centerY >>> " + mBitmapBound.centerY());
  canvas.drawCircle(mBitmapBound.centerX(), mBitmapBound.centerY(), mRadius, mFillPaint);
  // 绘制边框
  canvas.drawCircle(mBorderBound.centerX(), mBorderBound.centerY(), mRadius, mBoundPaint);
 }

 /**
  * 调整图片缩放,目前只支持CenterCrop
  */
 private void updateShaderMatrix() {
  float scale;
  float dx = 0;
  float dy = 0;

  mShaderMatrix.set(null);

  // 调整缩放,使图片居中
  if (mBitmapWidth * mBitmapBound.height() > mBitmapBound.width() * mBitmapHeight) {
   scale = mBitmapBound.height() / (float) mBitmapHeight;
   dx = (mBitmapBound.width() - mBitmapWidth * scale) * 0.5f;
  } else {
   scale = mBitmapBound.width() / (float) mBitmapWidth;
   dy = (mBitmapBound.height() - mBitmapHeight * scale) * 0.5f;
  }

  Log.d(TAG, "updateShaderMatrix: scale >>> " + scale);
  mShaderMatrix.setScale(scale, scale);
  // TODO: 16-10-15 http://chroya.iteye.com/blog/713869
  // 回到中心点,便于下次缩放
  mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBitmapBound.left, (int) (dy + 0.5f) + mBitmapBound.top);

  mShader.setLocalMatrix(mShaderMatrix);
 }
}

目前仍有点问题,解决后会及时更新。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android开发中TextView 实现右上角跟随文本动态追加圆形红点

    在一个比较坑的需求里,一段文字右上角需要追加一个圆形红点.最右侧有个金额,红点动态随着文字移动,然后各种摆布局,一下午坑死我了.后来果断放弃.然后就想试试直接自定义view来实现这个需求. 最坑的就是效果下面的第一种情况和第二种情况,就是这两种情况给逼的 废话不说,开搞. 首先自定义个view 继承自 view 类 public class MyViewAndCircle extends View{ } 然后不用说了 ,直接飘红,必须要实现几个必要的方法了. public MyViewAndCi

  • Android实现圆角矩形和圆形ImageView的方式

    Android中实现圆角矩形和圆形有很多种方式,其中最常见的方法有ImageLoader设置Option和自定义View. 1.ImageLoader加载图片 public static DisplayImageOptions getRoundOptions() { DisplayImageOptions options = new DisplayImageOptions.Builder() // 是否设置为圆角,弧度为多少,当弧度为90时显示的是一个圆 .displayer(new Round

  • Android自定义控件之圆形、圆角ImageView

    一.问题在哪里? 问题来源于app开发中一个很常见的场景--用户头像要展示成圆的:  二.怎么搞? 机智的我,第一想法就是,切一张中间圆形透明.四周与底色相同.尺寸与头像相同的蒙板图片,盖在头像上不就完事了嘛,哈哈哈! 在背景纯色的前提下,这的确能简单解决问题,但是如果背景没有这么简单呢? 在这种不规则背景下,有两个问题: 1).背景图常常是适应手机宽度缩放,而头像的尺寸又是固定宽高DP的,所以固定的蒙板图片是没法保证在不同机型上都和背景图案吻合的. 2).在这种非纯色背景下,哪天想调整一下头像

  • Android布局自定义Shap圆形ImageView可以单独设置背景与图片

    一.图片预览: 一.实现功能: 需求要实现布局中为圆形图片,图片背景与图标分开且合并到一个ImageView. 二.具体实现: XML中布局中定义ImageView,关健设置两个参数 Android:backgroup(设置背景),Android:src(设置ImageVIew中图片),圆形图片制作Drawable下定义xml shap样式(solid-color,size-width\hight) XML代码如下: <ImageView android:id="@+id/zhongjie

  • Android使用CircleImageView实现圆形头像的方法

    有时我们在应用中会用到圆形头像,下面是利用CircleImageView实现圆形头像的演示,下面效果和代码,效果如图 实现起来也比较简单,先在项目中建一个circleimageview包用来存放CircleImageView类,待会直接把CircleImageView类复制到包里就可以使用了 然后,再建一个attrs.xml,其代码相当简单,定义了圆形边框宽度和颜色 <?xml version="1.0" encoding="utf-8"?> <r

  • Android利用CircleImageView实现圆形头像的方法

    CircleImageView实现圆形头像代码分享,供大家参考,具体内容如下 一.创建属性文件(attrs.xml) 具体操作: 1.在项目的values文件底下创建一新的属性文件,文件名为attrs:New->XML->Values XML File:  2.补充attrs.xml代码: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleabl

  • Android中使用CircleImageView和Cardview制作圆形头像的方法

    圆形头像在我们的日常使用的app中很常见,因为圆形的头像比较美观. 使用圆形图片的方法可能有我们直接将图片裁剪成圆形再在app中使用,还有就是使用自定义View对我们设置的任何图片自动裁剪成圆形. 效果图: 这里使用github上CircleImageView github:https://github.com/hdodenhof/CircleImageView CardView顾名思义卡片式的View,CardView继承的是FrameLayout,所以摆放内部控件的时候需要注意一下 可以设置

  • Android项目实战手把手教你画圆形水波纹loadingview

    本文实例讲解的是如何画一个满满圆形水波纹loadingview,这类效果应用场景很多,比如内存占用百分比之类的,分享给大家供大家参考,具体内容如下 效果图如下: 预备的知识: 1.贝塞尔曲线    如果你不了解,可以来这里进行基础知识储备:神奇的贝塞尔曲线 2.Paint.setXfermode()  以及PorterDuffXfermode 千万不要被这个b的名字吓到,不熟悉看到可能会认为很难记,其实 只要站在巨人的丁丁上 还是很简单的. 好了 废话不多说 ,跟我一步步来做一个炫酷的view吧

  • Android自定义view实现圆形与半圆形菜单

    前不久看到鸿洋大大的圆形菜单,就想开始模仿,因为实在是太酷了,然后自己根据别人(zw哥)给我讲的一些思路.一些分析,就开始改造自己的圆形菜单了. 文章结构:1.功能介绍以及展示:2.部分代码讲解:3.大致可以实现的UI效果展示讲解.4.源码附送. 一.功能介绍以及展示 第一个展示是本控件的原样.但是我们可以使用很多技巧去达到我们的商业UI效果嘛. 这里给出的是本博客作品demo的展示图以及第三点的联动展示,可见是一圆型菜单,相较于鸿洋大大的那个圆形菜单多了一些需求: 1.到时候展示只需要半圆的转

  • Android 自定义圆形头像CircleImageView支持加载网络图片的实现代码

    在Android开发中我们常常用到圆形的头像,如果每次加载之后再进行圆形裁剪特别麻烦.所以在这里写一个自定义圆形ImageView,直接去加载网络图片,这样的话就特别的方便. 先上效果图 主要的方法 1.让自定义 CircleImageView 继承ImageView /** * 自定义圆形头像 * Created by Dylan on 2015/11/26 0026. */ public class CircleImageView extends ImageView { } 2.在构造方法中

随机推荐