Android 自定义View学习(五)——Paint 关于PorterDuffXfermode学习

学习资料:

  • Android群英传

1.PorterDuffXfermode

PorterDuffXfermode有点类似数学中的交集,并集,用来两个图像间的混合显示模式,设置的是两个图层交集区域的显示方式,dst是下层,先画的图形;src是上层,后画的图形

构造方法:

  • PorterDuffXfermode(PorterDuff.Mode mode)

构造方法中只需一个参数,PorterDuff.ModeModePorterDuff这个类中的一个枚举类,现在Mode中有18个枚举值,图中只有16个,图少了ADDOVERLAY


2.PorterDuff.Mode

Android 自定义View学习(五)——Paint 关于PorterDuffXfermode学习_第1张图片
PorterDuff.Mode错误图

上面的图是有错误的,下面的图,和我自己测试的结果是一致的。错误原因可以去Android中Canvas绘图之PorterDuffXfermode使用及工作原理详解看看

Android 自定义View学习(五)——Paint 关于PorterDuffXfermode学习_第2张图片
修正后的PorterDuff.Mode

最常用的就是DST_INSRC_IN,是可以用来实现自定义CircleImageView的一种方式

看图帮助:后来者居上

  • 黄色的圆是DST下层,先进行绘制;
  • 蓝色的矩形是SRC上层,后进行绘制
模式 效果
PorterDuff.Mode.CLEAR 上层绘制不会提交到画布,并把与下层交集部分也清除
PorterDuff.Mode.SRC 显示上层绘制,此时下层绘制也会显示
PorterDuff.Mode.DST 显示下层绘制,而上层不会绘制
PorterDuff.Mode.SRC_OVER 正常显示,上层叠盖在下层之上
PorterDuff.Mode.DST_OVER 上下层都显示,下层在上
PorterDuff.Mode.SRC_IN 将交集显示在下层绘制的区域
PorterDuff.Mode.DST_IN 显示下层绘制
PorterDuff.Mode.SRC_OUT 取非交集区域
PorterDuff.Mode.DST_OUT 取下层非交集区域
PorterDuff.Mode.SRC_ATOP 取上层的交集区域和下层的非交集区域
PorterDuff.Mode.DST_ATOP 下层在上层之上
PorterDuff.Mode.XOR 去除两层的交集区域
PorterDuff.Mode.DARKEN 取两层全部区域,交集区域变暗
PorterDuff.Mode.LIGHTEN 取两层全部区域,交集区域变亮
PorterDuff.Mode.MULTIPLY 取下层全部区域,交集区域色彩叠加,正片叠底
PorterDuff.Mode.SCREEN 取两层全部区域,交集区域变透明
PorterDuff.Mode.ADD 饱和度叠加
PorterDuff.Mode.OVERLAY 对黑白无效,显示两层颜色中和后的中间色

PorterDuff.Mode不单单是进行了图形的操作,有的模式还对色彩有影响

这些模式最好都自己测试一下

具体的过程可以学习爱哥的自定义控件其实很简单1/6

有的模式,不支持硬件加速,最好关闭硬件加速

绘制过程后来者居上,dst为下层,先进行绘制;src为上层,后进行绘制。

生活中的例子: 鸡蛋灌饼 : )


简单测试:

Android 自定义View学习(五)——Paint 关于PorterDuffXfermode学习_第3张图片
PorterDuff.Mode.MULTIPLY
public class PorterDuffView extends View {
    private Paint paint ;
    private RectF rectF;
    private PorterDuffXfermode porterDuffXfermode;
    public PorterDuffView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    private void initPaint() {
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);//关闭硬件加速
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rectF = new RectF(150,150,500,500);
       porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);
    }

    @Override
  protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制背景色
        canvas.drawColor(Color.YELLOW);
        //创建一个新的画布Layer
        int layerId = canvas.saveLayer(0, 0, getWidth(),getHeight() , null, Canvas.ALL_SAVE_FLAG);
        //绘制dst层
        float x = getWidth() / 4;
        float y = getHeight() / 4;
        float radius = Math.min(getWidth(), getHeight()) / 4;
        paint.setColor(Color.CYAN);
        canvas.drawCircle(x, y, radius, paint);
        //设置混合模式
         paint.setColor(Color.RED);
        paint.setXfermode(porterDuffXfermode);
        //绘制src层
        canvas.drawRect(rectF,paint);
        paint.setXfermode(null);
        canvas.restoreToCount(layerId);//将自己创建的画布Layer绘制到画布默认的Layer
    }


    /**
     * 测量
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, 300);
        } else if (wSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, hSpecSize);
        } else if (hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(wSpecSize, 300);
        }
    }
}

之所以更改图层是为了让绘制不受背景色影响。将绘制的圆形和矩形在一个新的图层绘制,绘制完成后,再把新的图层加入到Canvas的默认图层。


3.CircleImageView

如果只是单单为了显示一个圆形的图片,可以有好多种办法。

可以借助PorterDuffXfermode或者v4包下的RoundedBitmapDrawable进行把Biamp进行形状上的改变得到圆形

若不是经常大量的频繁的显示出一个圆形图片,也可以利用CardView,Android 一个另类的显示圆形图片方式

本篇为了学习PorterDuffXfermode就使用前面提到的PorterDuff.Mode.SRC_IN


简单实践:


Android 自定义View学习(五)——Paint 关于PorterDuffXfermode学习_第4张图片
圆形图片
public class CircleImageView extends ImageView {

    private Paint mPaint;

    public CircleImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();

    }

    private void initPaint() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        BitmapDrawable drawable = (BitmapDrawable) getDrawable();
        if (drawable != null) {
            Bitmap bitmap = drawable.getBitmap();
            drawTargetBitmap(canvas, bitmap);
        } 
    }


    private void drawTargetBitmap(Canvas canvas, Bitmap bitmap) {
        final int sc = canvas.saveLayer(0, 0, getWidth(),getHeight(), null, Canvas.ALL_SAVE_FLAG);
        //先绘制dst层
        final float x = getWidth() / 2;
        final float y = getHeight() / 2;
        final float radius = Math.min(getWidth(), getHeight()) / 2;
        canvas.drawCircle(x, y, radius, mPaint);
        //设置混合模式
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        //绘制src层
        final float f_x = getWidth()  / 2 -bitmap.getWidth() / 2;
        final float f_y = getHeight() / 2 -bitmap.getHeight() / 2;
        canvas.drawBitmap(bitmap, f_x,f_y, mPaint);
        // 还原混合模式
        mPaint.setXfermode(null);
        // 还原画布
        canvas.restoreToCount(sc);
    }
}

一个超级简单的自定义CircleImageView


4.最后

简单介绍完毕,主要就是18种模式的理解,最好每种模式的效果都能够明白,掌握最常用的DST_INSRC_IN

进一步学习,可以看鸿洋大神的Android 自定义控件实现刮刮卡效果 真的就只是刮刮卡么


5. 补充

9月9号 晚上

关于PorterDuffXfermode的坑总结的很好,PorterDuffXfermode不正确的真正原因


本人还很菜,有错误,请指出

共勉 : )

你可能感兴趣的:(Android 自定义View学习(五)——Paint 关于PorterDuffXfermode学习)