PorterDuff.Mode

有的时候我们做UI上的效果不可避免用到PorterDuff.Mode,官方给出的解释是定义了源图和目标图的合成结果,我们可以理解为两个图片的不同叠加所组成的效果,根据我们日常设计的需求通过算法给出了不同的模式


1.概述

官方给出了18种模式

首先来个所有模式的总图。

官方demo

public class Xfermodes extends GraphicsActivity {
    // create a bitmap with a circle, used for the "dst" image
    static Bitmap makeDst(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setColor(0xFFFFCC44);
        c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);
        return bm;
    }
    // create a bitmap with a rect, used for the "src" image
    static Bitmap makeSrc(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setColor(0xFF66AAFF);
        c.drawRect(w/3, h/3, w*19/20, h*19/20, p);
        return bm;
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new SampleView(this));
    }
    private static class SampleView extends View {
        private static final int W = 64;
        private static final int H = 64;
        private static final int ROW_MAX = 4;   // number of samples per row
        private Bitmap mSrcB;
        private Bitmap mDstB;
        private Shader mBG;     // background checker-board pattern
        private static final Xfermode[] sModes = {
            new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
            new PorterDuffXfermode(PorterDuff.Mode.SRC),
            new PorterDuffXfermode(PorterDuff.Mode.DST),
            new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
            new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
            new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
            new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
            new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
            new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
            new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
            new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
            new PorterDuffXfermode(PorterDuff.Mode.XOR),
            new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
            new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
            new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
            new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
        };
        private static final String[] sLabels = {
            "Clear", "Src", "Dst", "SrcOver",
            "DstOver", "SrcIn", "DstIn", "SrcOut",
            "DstOut", "SrcATop", "DstATop", "Xor",
            "Darken", "Lighten", "Multiply", "Screen"
        };
        public SampleView(Context context) {
            super(context);
            mSrcB = makeSrc(W, H);
            mDstB = makeDst(W, H);
            // make a ckeckerboard pattern
            Bitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,
                                            0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,
                                            Bitmap.Config.RGB_565);
            mBG = new BitmapShader(bm,
                                   Shader.TileMode.REPEAT,
                                   Shader.TileMode.REPEAT);
            Matrix m = new Matrix();
            m.setScale(6, 6);
            mBG.setLocalMatrix(m);
        }
        @Override protected void onDraw(Canvas canvas) {
            canvas.drawColor(Color.WHITE);
            Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
            labelP.setTextAlign(Paint.Align.CENTER);
            Paint paint = new Paint();
            paint.setFilterBitmap(false);
            canvas.translate(15, 35);
            int x = 0;
            int y = 0;
            for (int i = 0; i < sModes.length; i++) {
                // draw the border
                paint.setStyle(Paint.Style.STROKE);
                paint.setShader(null);
                canvas.drawRect(x - 0.5f, y - 0.5f,
                                x + W + 0.5f, y + H + 0.5f, paint);
                // draw the checker-board pattern
                paint.setStyle(Paint.Style.FILL);
                paint.setShader(mBG);
                canvas.drawRect(x, y, x + W, y + H, paint);
                // draw the src/dst example into our offscreen bitmap
                int sc = canvas.saveLayer(x, y, x + W, y + H, null);
                canvas.translate(x, y);
                canvas.drawBitmap(mDstB, 0, 0, paint);
                paint.setXfermode(sModes[i]);
                canvas.drawBitmap(mSrcB, 0, 0, paint);
                paint.setXfermode(null);
                canvas.restoreToCount(sc);
                // draw the label
                canvas.drawText(sLabels[i],
                                x + W/2, y - labelP.getTextSize()/2, labelP);
                x += W + 10;
                // wrap around when we've drawn enough for one row
                if ((i % ROW_MAX) == ROW_MAX - 1) {
                    x = 0;
                    y += H + 30;
                }
            }
        }
    }
}

运行结果

2.日常应用

日常应用中我们会用它两两模式的混合,把一个矩形图片变为圆角图片,变为圆形图片等。

public class RoundRectView extends View {

    private Bitmap mBitmap;
    private Bitmap mOut;
    private Paint mPaint;


    public RoundRectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public void initView() {
        mBitmap = BitmapFactory.decodeResource(getResources(),
                R.drawable.a1);

        mOut = Bitmap.createBitmap(mBitmap.getWidth(),
                mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(mOut);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        canvas.drawRoundRect(0, 0,
                mBitmap.getWidth(),
                mBitmap.getHeight(), 80, 80, mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(
                PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(mBitmap, 0, 0, mPaint);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mOut, 0, 0, null);
    }
}

运行结果

把canvas.drawRoundRect 替换为

canvas.drawCircle(mBitmap.getWidth()/2,mBitmap.getHeight()/2,mBitmap.getWidth()/2,mPaint);

就变成了

3.剖析

开头我们说明了PorterDuffXfermode设置的是两个图层交集区域的显示方式,dst是先画的图层,src是后画的图层。
在RoundRectView中我们的大致实现思路是先用普通画笔画一个圆角遮罩,再用带PorterDuffXfermode的画笔将这个美女的图片画在遮罩上,两者混合就是我们看到的效果。

具体剖析

① 在initView()方法中先得到了Bitmap对象美女图片mBitmap,然后根据美女图片mBitmap的宽高创建了一个Bitmap对象mOut。
② Canvas canvas = new Canvas(mOut) 为什么创建Canvas对象时要传入一个Bitmap对象,这是因为这样做是此Bitmap会和canvas紧紧联系在一起,在canvas.drawXXX的所有操作都是产生在此Bitmap上的。

③接下来就是实例化了一个画笔,设置抗锯齿,然后在mOut这个Bitmap上绘制了一个圆角矩形,设置 Xfermode混合方式,再在mout这个Bitmap上绘制美女图,这个时候mout就才真正成为了一个圆角美女图。但是我们还没有把它绘制出来,它只是存在于初始化当中,我们在重写的onDraw方法中canvas.drawBitmap(mOut, 0, 0, null),这个时候我们就能看到圆角矩形美女图了。

posted @ 2020-08-11 09:50  我的小鱼干嘞  阅读(1200)  评论(0编辑  收藏  举报