前言
上一篇文章《 圆形ImageView系列(一)—–Xfermode+View》介绍了Xfermode,并且结合View实现了圆形ImageView,这种方式实现的思路和方法都很简单,但是使用起来总感觉少了点什么。
既然是圆形ImageView,但是原生的ImageView的很多属性却使用不了,比如scaleType等等,所以下面我们使用第二种方案。
主要原理
在ImageView的上面覆盖一个遮罩层,将这个遮罩层的中间抠掉一个圆形之后,整个ImageView看起来就能显示出圆形的效果了。
先来看看效果图。
上面那张效果图里,下面的两张图可以明显的看到有个白色的遮罩层覆盖在ImageView上面,所以看起来就显示成了圆形ImageView的效果。
具体实现
package com.passerby.androidadvanced.circleimage.imageview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* Created by mac on 16/2/3.
*/
public class CircleLayerImageView extends ImageView {
public CircleLayerImageView(Context context) {
this(context, null);
}
public CircleLayerImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleLayerImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
//创建跟imageview相同宽高的遮罩层
int min = Math.min(width, height);
Bitmap bitmapMask = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvasMask = new Canvas(bitmapMask);
//遮罩层颜色
int maskColor;
Drawable background = getBackground();
if (background != null && background instanceof ColorDrawable) {
maskColor = ((ColorDrawable) background).getColor();
} else {
maskColor = Color.WHITE;
}
canvasMask.drawColor(maskColor);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
canvas.saveLayer(0, 0, width, height, paint, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(bitmapMask, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawCircle(width >> 1, height >> 1, min >> 1, paint);
canvas.restore();
}
}
看到这么少的代码是不是吓尿了,之所以这么少是因为是直接继承的ImageView,很多工作ImageView已经帮我们完成了。
代码第34行,要记得调用父类的onDraw方法,不然imageView原本的图片没法正常显示。
第39~42行,创建了一个跟imageView相同大小的遮罩层Bitmap,新建了一个canvas并且将bitmap作为参数传进去。注意这里是新建的一个canvas,而不是直接使用onDraw里面系统传入的canvas,因为我们只需要对这个遮罩bitmap画上颜色而已,如果直接使用系统的canvas,那遮罩层的颜色就画在imageView上面而不是遮罩bitmap上面了。
第44~54行,主要设置遮罩层的颜色,这个颜色从ImageView的background属性中获取,这样就不会使遮罩层显得十分突兀,比如你可以看看上面效果图里面我做了一个actionBar的效果,将圆形ImageView的background跟外面的布局颜色设置成一样,看上去效果就非常好了。
当然为了容错,有人可能不小心将background设置成了图片或者其他drawable,这里默认就设置成白色的遮罩层颜色。
第60~68行,一共5行代码,主要用到了canvas的layer知识。了解photoshop的同学对图层的概念应该非常熟悉,包括我们常用的地图软件也有很多不同的图层,具体知识请百度,这里只需要注意canvas.saveLayer和canvas.restore要成对使用就好。
第62行,将遮罩层画在新的图层上,遮罩层将作为src。
第64行,改变画笔的xfermode模式,对照之前的表,应该选取srcOut模式。
第66行,将适当大小的圆画在遮罩层上面,结合画笔的xfermode模式,画完之后的效果相当于遮罩层被抠掉了一个圆形,位于遮罩层之下的imageview就显示成圆形的效果了。