之前认为圆形头像非常难实现,系统为啥不提供一个属性来支持一下圆角呢?后来在做项目的时候就一直用大神的开源库。
https://github.com/hdodenhof/CircleImageView,这是一个很方便的库,所以就没有再研究圆形头像。
后来突然要学习自定义View就一起研究了一下。当然实现圆角头像的方法有很多,我这里只列举其中两种。
Xfermode
第一种实现方案是XferMode。
Google提供了一系列的Mode来实现图层叠加的不同效果,这一功能跟PhotoShop等软件中的布尔运算是差不多的。
光看图有点抽象,我们看看圆形头像场景下,我们要用到哪种模式。
首先我粗略的实现了一个效果,简单的看一下代码。
public class CircleHeadXferMode extends View {
private Paint mPaint;
private Bitmap bitmap;
private int width;
private int height;
private Bitmap out;
public CircleHeadXferMode(Context context) {
super(context);
init(context);
}
public CircleHeadXferMode(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CircleHeadXferMode(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
setLayerType(LAYER_TYPE_SOFTWARE, null);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.a);
width = bitmap.getWidth();
height = bitmap.getHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(width/2,width/2,width/2,mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap,0,0,mPaint);
}
}
这里我直接继承自View,实现了一个自定义View,当然初始化方法这里我就不赘述了。
init方法中也是一些简单的初始化操作,bitmap是在一个本地资源中加载的,获取他的宽高作为后面绘制的尺寸,由于这里我用的是一个方形图片,所以没有对尺寸做特殊处理。
在onDraw方法中,先绘制了一个圆形,这个就是我们圆形头像的边界,然后我设置XferMode,这里选择的是一个SRC_IN的形式。
根据上面的Mode图片,可以看到SrcIn是取了两个图层的子集,其中Src和Dst分别是两个图层,关于SrcIn和DstIn的区别我们后面看效果再说。
最后绘制了图像。看一下效果。
对于DstIn是这种效果(画笔默认的黑色)。
所以可以看到,对于SRCIN和DSTIN就是对于图层调整了位置的交集,前者是后写的在上面,后者是前写的在上面。
对于其他的尺寸缩放的问题这里不再赘述,主要是看看如何实现一个圆形头像。
Clip
第二种方法同样很简单,是借助于一个canvas的clip功能间接实现的圆形。
public class CircleHead extends View {
private Bitmap bitmap;
private Paint paint;
private Path mPath;
public CircleHead(Context context) {
super(context);
init(context);
}
public CircleHead(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CircleHead(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
//禁用硬件加速功能
setLayerType(LAYER_TYPE_SOFTWARE, null);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.a);
paint = new Paint();
mPath = new Path();
int width = bitmap.getWidth();
int height = bitmap.getHeight();
mPath.addCircle(width / 5, height / 5, width / 5, Path.Direction.CCW);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
//裁剪为圆形路径
canvas.clipPath(mPath);
canvas.drawBitmap(bitmap,0,0,paint);
}
}
继承自View,在init方法中初始化了一些内容。
首先,禁用硬件加速。
setLayerType(LAYER_TYPE_SOFTWARE, null);
硬件加速开启的时候,会对clip产生影响。
其次获取bitmap,宽高。
然后直接构建一个圆形路径,方向为CCW(逆时针),
最后在onDraw()方法中裁剪圆形路径,并将我们的头像资源绘制上去。
实现的效果图跟上面贴的一样。
总结
实现圆形图片的方法有很多,比如还有Glide,但是Glide处理不好,图片会有缩放不一致的情况,自行缩放可以选择Shader,选择一种比较喜欢的即可。
ok,就到这里啦。
网友评论