圆形头像的两种实现方法

2019-09-29  本文已影响0人  做梦枯岛醒

之前认为圆形头像非常难实现,系统为啥不提供一个属性来支持一下圆角呢?后来在做项目的时候就一直用大神的开源库。
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,就到这里啦。

上一篇 下一篇

猜你喜欢

热点阅读