三种方式实现圆角图片

最近在学习自定义view的时候看见了几个方法,一个是clipxxx(),一个是setShader(),一时有点懵,学习了一下,感觉还是很简单的,和xfermode有异曲同工之妙,所以写了个demo,来看看最基本的方法
为了表现最基本的用法,不考虑图片的适配缩放,不考虑内存泄漏等情况,给最直观的用法

三种方式实现圆角图片_第1张图片

clipxxx()实现圆角图片

 private void drawRoundImageByPath() {
        mCanvas.save();
        mPath.addCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, 90, Path.Direction.CCW);
        //由于我们只是需要将画布裁剪成圆形,无需考虑两种图形相交情况,所以OP参数任意
        mCanvas.clipPath(mPath, Region.Op.REVERSE_DIFFERENCE);
        mCanvas.restore();

    }

mCanvas是onDraw函数里默认的canvas,关于clipxxx(),个人理解
剪裁画布,因为我们需要的是一个圆形,所以只需要将path设置为圆形然后剪裁即可,有两点需要注意的是,clipxxx()剪裁的是画布,所以我们需要什么形状,只需要将画布剪裁成相应形状然后画view即可。也就是说先剪裁,后画图,当然你也可以先画图后剪裁,但是这样不符合惯性思维
更多属性以及用法请参考 http://blog.csdn.net/wzlyd1/article/details/72436585

Shader方法实现圆角图片

//shader实现圆角图片,记得屏蔽super.onDraw(canvas)
    private void drawRoundImageByShader() {
        if (mSharder == null) {
            mSharder = new BitmapShader(getBitmapBeforeDraw(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        }
        mPaint.setShader(mSharder);
        mCanvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, 90, mPaint);
    }

主要的知识点

tileX The tiling mode for x to draw the bitmap in. 在位图上X方向花砖模式

tileY The tiling mode for y to draw the bitmap in. 在位图上Y方向花砖模式

TileMode:(一共有三种)

CLAMP :如果渲染器超出原始边界范围,会复制范围内边缘染色。

REPEAT :横向和纵向的重复渲染器图片,平铺。

MIRROR :横向和纵向的重复渲染器图片,这个和REPEAT 重复方式不一样,他是以镜像方式平铺。

Xfermode实现圆角图片

private void drawRoundImageByXfermode() {

        int saveLayerId = mCanvas.saveLayer(0, 0, mCanvas.getWidth(), mCanvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);
        //先画DST
        mCanvas.drawBitmap(getBitmapBeforeDraw(), 0, 0, mPaint);
        //画SRC
        Bitmap bitmap = Bitmap.createBitmap(mCanvas.getWidth(), mCanvas.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas ca = new Canvas(bitmap);
        ca.drawCircle(mCanvas.getWidth() / 2, mCanvas.getHeight() / 2, 90, mPaint);

        //相交取下半部分
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        mCanvas.drawBitmap(bitmap, 0, 0, mPaint);
        mPaint.setXfermode(null);

        mCanvas.restoreToCount(saveLayerId);
    }

主要知识点在于 PorterDuff.Mode的几种模式,以及你要分清楚什么是DST 什么是SRC,这点很重要,更多可以参考博客 http://blog.csdn.net/wzlyd1/article/details/71597599

完整代码

package view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;


/**
 * 简单起见,固定宽高为200dp
 */

public class RoundImageView extends ImageView {

    private Paint mPaint;
    private Paint mDSTPaint;
    private BitmapShader mSharder;
    private Path mPath;
    private Canvas mCanvas;

    private PorterDuffXfermode mMode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);

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

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

    public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setAntiAlias(true);
        mDSTPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mDSTPaint.setAntiAlias(true);
        mPath = new Path();
        setLayerType(LAYER_TYPE_SOFTWARE, mPaint);//clipPath 不支持硬件加速

    }

    @Override
    protected void onDraw(Canvas canvas) {
        mCanvas = canvas;
//        drawRoundImageByXfermode();
//        drawRoundImageByShader();
//        drawRoundImageByPath();


    }
    //通过路径来实现圆角,注意super.onDraw(canvas)调用位置
    private void drawRoundImageByPath() {
        mCanvas.save();
        mPath.addCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, 90, Path.Direction.CCW);
        //由于我们只是需要将画布裁剪成圆形,无需考虑两种图形相交情况,所以OP参数任意
        mCanvas.clipPath(mPath, Region.Op.INTERSECT);
        mCanvas.restore();

    }

    //shader实现圆角图片,记得屏蔽super.onDraw(canvas)
    private void drawRoundImageByShader() {
        if (mSharder == null) {
            mSharder = new BitmapShader(getBitmapBeforeDraw(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        }
        mPaint.setShader(mSharder);
        mCanvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, 90, mPaint);
    }

    private void drawRoundImageByXfermode() {

        int saveLayerId = mCanvas.saveLayer(0, 0, mCanvas.getWidth(), mCanvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);
        //先画DST
        mCanvas.drawBitmap(getBitmapBeforeDraw(), 0, 0, mPaint);
        //画SRC
        Bitmap bitmap = Bitmap.createBitmap(mCanvas.getWidth(), mCanvas.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas ca = new Canvas(bitmap);
        ca.drawCircle(mCanvas.getWidth() / 2, mCanvas.getHeight() / 2, 90, mPaint);

        //相交取下半部分
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        mCanvas.drawBitmap(bitmap, 0, 0, mPaint);
        mPaint.setXfermode(null);

        mCanvas.restoreToCount(saveLayerId);
    }


    public Bitmap getBitmapBeforeDraw() {
        BitmapDrawable b = (BitmapDrawable) this.getDrawable();
        return Bitmap.createScaledBitmap(b.getBitmap(), getMeasuredWidth(), getMeasuredHeight(), false);
    }
}

总结

第一种方式最简单,但是剪切出来的图形有锯齿,图越大越明显,且设置抗锯齿没用~
第二种方式是Fresco 源码的设置方式,可将图形渲染为你指定的图形。
第三种方式比较复杂,尤其是要搞清楚哪个是DST ,哪个是SRC,相较于第二种,我觉得最大的不同点在于,第二种是将一种图渲染为一种指定的形状
第三种是两种图形的交汇来达到不同的效果

你可能感兴趣的:(自定义view学习系列)