时间: 2020-08-30|tag:75次围观|0 条评论

  圆角图片在日常开发是非常常见,所以掌握它也是必要。之前也掌握相关有效的方法,但是从来没有总结过,今天写一篇文章来专门总结已知的方法。本文会介绍三种实现圆角图片的方法,同时也会介绍他们直接的区别。

1. clipPath方法

  第一种方法通过CanvasclipPath来实现,我们先来看一下相关实现代码:

public class RoundImageViewByClipPath extends ImageView {  private Path mPath;  private PaintFlagsDrawFilter mPaintFlagsDrawFilter;  private RectF mRectF;  private float[] mRadius = new float[]{100, 100, 100, 100, 100, 100, 100, 100};  public RoundImageViewByClipPath(Context context) {    this(context, null);  }  public RoundImageViewByClipPath(Context context, @Nullable AttributeSet attrs) {    this(context, attrs, 0);  }  public RoundImageViewByClipPath(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    init();  }  @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)  public RoundImageViewByClipPath(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {    super(context, attrs, defStyleAttr, defStyleRes);    init();  }  private void init() {    mPath = new Path();    mPaintFlagsDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);  }  @Override  protected void onDraw(Canvas canvas) {    if (mRectF == null) {      mRectF = new RectF();      mRectF.set(0, 0, getMeasuredWidth(), getMeasuredHeight());    }    mPath.addRoundRect(mRectF, mRadius, Path.Direction.CW);    canvas.setDrawFilter(mPaintFlagsDrawFilter);    canvas.clipPath(mPath);    super.onDraw(canvas);  }}

  clipPath方法会将绘制的图形裁剪下来,我们向Path中添加了一个带圆角的矩形,所以后面绘制的图片自然而然就变成了圆角图片。
  这种方法的优缺点也是非常明显的:

  1. 优点:简单。
  2. 缺点:绘制出来的圆角图片带锯齿。这一点是非常不能忍,所以我们通常不会用此方式来实现圆角图片。

2. Xfermode方法

  接下来,我们来看一下第二种方式--通过Xfermode来实现圆角图片。我们先来看看代码实现:

public class RoundedImageViewByXfermode extends ImageView {  private Paint mPaint;  private Xfermode mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);  private RectF mRectF;  private Bitmap mBitmap;  public RoundedImageViewByXfermode(Context context) {    this(context, null);  }  public RoundedImageViewByXfermode(Context context, @Nullable AttributeSet attrs) {    this(context, attrs, 0);  }  public RoundedImageViewByXfermode(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    init();  }  @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)  public RoundedImageViewByXfermode(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {    super(context, attrs, defStyleAttr, defStyleRes);  }  private void init() {    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);  }  @Override  protected void onDraw(Canvas canvas) {    canvas.saveLayer(0, 0, getMeasuredWidth(), getMeasuredHeight(), null, Canvas.ALL_SAVE_FLAG);    Drawable drawable = getDrawable();    if (mBitmap == null) {      mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);      Canvas drawCanvas = new Canvas(mBitmap);      drawable.draw(drawCanvas);    }    if (mRectF == null) {      mRectF = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());    }    mPaint.setXfermode(null);    canvas.drawRoundRect(mRectF, 100, 100, mPaint);    mPaint.setXfermode(mXfermode);    canvas.drawBitmap(mBitmap, 0, 0, mPaint);    canvas.restore();  }}

  其实呢,使用Xfermode实现圆角图片的原理非常简单,先绘制Dst图层和src图层,然后根据谷歌爸爸给定的规则设置不同的Xfermode,从而达到裁剪图层的效果。也就是下面这张图片:

Android 自定义View – 实现圆角图片的几种方式插图

  使用Xfermode通常来说会绘制两层,第一层称为Dst图层,第二层称为src图层,然后根据Xfermode来去计算两个图层的关系。
  canvas有一个默认的layer,平时我们调用的drawXXX方法所绘制的内容都是这个layer上面。而使用Xfermode需要注意的地方是,我们直接将内容绘制在这个默认的layer上面,而是必须绘制在一个新的layer上面,然后通过restore或者restoreToCount将新layer的内容绘制在默认的layer上面,如此操作才是正确的;除此之外,也可以通过新的bitmap来实现。具体的介绍,可以参考:Android PorterDuffXfermode使用中的一些坑
  而这种方法的优缺点呢?

  1. 优点:可定义高,支持多图层的圆角。
  2. 缺点:麻烦。

3. BitmapShader

  最后一种方法就是通过BitmapShader方法来实现,这种方法也是极力推荐,如果你的要求仅仅是图片实现圆角,推荐这种方法,为什么呢?我们先来看看相关实现:

public class RoundImageViewByBitmapShader extends ImageView {  private Shader mShader;  private Paint mPaint;  public RoundImageViewByBitmapShader(Context context) {    super(context);  }  public RoundImageViewByBitmapShader(Context context, @Nullable AttributeSet attrs) {    super(context, attrs);  }  public RoundImageViewByBitmapShader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);  }  @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)  public RoundImageViewByBitmapShader(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {    super(context, attrs, defStyleAttr, defStyleRes);  }  @Override  protected void onDraw(Canvas canvas) {    BitmapDrawable drawable = (BitmapDrawable) getDrawable();    if (mShader == null) {      mShader = new BitmapShader(drawable.getBitmap(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);      mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);    }    mPaint.setShader(mShader);    canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), 100, 100, mPaint);  }}

  具体就是上面的,至于为什么这样就能实现圆角,大家可以参考这篇文章:Android之Shader完全理解指南

  1. 优点:简单并且有效。
  2. 确定:仅支持Bitmap圆角,如果是LayerDrawable就显得手足无措。

5. 总结

  就目前而言,常见的实现圆角的方式就是上面三种,通常来说,我们选择第2种或者第3种方法,而第一种方法不推荐使用。
  而如果你的圆角要求仅限于纯图片,推荐第3种方式,因为比较简单;如果需要支持复杂的情形,比如LayerDrawable,推荐使用第2种方式。

文章转载于:https://www.jianshu.com/p/5d4909e72153

原著是一个有趣的人,若有侵权,请通知删除

本博客所有文章如无特别注明均为原创。
复制或转载请以超链接形式注明转自起风了,原文地址《Android 自定义View – 实现圆角图片的几种方式
   

还没有人抢沙发呢~