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

学习资料:

  • Android群英传

1.PorterDuffXfermode

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

构造方法:

  • PorterDuffXfermode(PorterDuff.Mode mode)

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


2.PorterDuff.Mode

PorterDuff.Mode错误图

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

修正后的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为上层,后进行绘制。

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


简单测试:

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进行形状上的改变得到圆形

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

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


简单实践:


圆形图片
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不正确的真正原因


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

共勉 : )

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容