Paint API - Detailed explanation of Xfermode and PorterDuff (4)


Introduction to this section:

In the previous section we wrote about the first example of using Xfermode and PorterDuff: the implementation of rounded corners & circular image ImageView, We have realized the benefits that PorterDuff.Mode.DST_IN has brought to us. In this section we will continue to write examples to practice. Do you still remember the implementation of pulling off the clothes of a beautiful woman brought to you by 8.3.2 Drawing Practical Example?

1.gif

Our implementation plan at that time was to set 20*20 pixels near the finger touch area to be transparent. The rendering is like this:

2.gif

I don’t know Have you noticed a problem? When we wipe the clothes of beautiful women, they are all square, but when we draw the drawing board When drawing a picture, the lines are very smooth. Is there a way to combine the two so that when we wipe the clothes, they are also smooth? The answer is definitely yes, just use Xfermode! In this section we use another mode, DST_OUT mode! Draw target graphs in disjoint places

3.png

If you forget a certain mode or haven’t even seen the 18 modes, then please move to:Android basic introductory tutorial-8.3 .5 Paint API - Detailed explanation of Xfermode and PorterDuff (2) In addition, I still want to post the rendering of PorterDuff.Mode:

4.png

Well, without further ado, let’s start this section ~


1. The renderings to be realized and the analysis of the implementation process:

The renderings to be realized:

5.gif

7.gifWell, I don’t know how many times you have seen that Gif? I don’t know if the picture suits everyone’s taste, Xiaozhu It was scraped from someone else's APP. Don't ask me for my phone number or email address. I don't know anything. I'm looking for something. Ask the veteran driver in the group - Ji Shen, okay, let's analyze the implementation process~

  • Let’s talk about the principle. It’s actually two Bitmaps, one in front and one behind. The one in the front is wearing clothes, and the one in the back is unclothed. Then use a Path to record the graphics drawn by the user, and then set the DST_OUT mode for our brush, then The DST (target image) that overlaps with Path, which is the image wearing clothes, will become transparent! Okay, that’s easy! Let’s refine it slowly!
  • First we need two Bitmaps to store the two pictures before and after. Here we make both Bitmaps full screen!
  • Then set the brush, rounded corners, pen width, anti-aliasing, etc.!
  • Then define a drawing Path, which is the method for the user to draw the area. Just draw the area after setting Xfermode!
  • Then rewrite the onTouchEvent method. This part is the same as the previous custom drawing board!
  • Finally rewrite the onDraw() method, first draw the background image, call the user's method of drawing the area, and then draw the foreground image!

It may look a bit complicated, but it is not. The code is super simple~


2. Code implementation:

Just one Custom View——StripMeiZi.java

/**
 * Created by Jay on 2015/10/25 0025.
 */
public class StripMeiZi extends View{

    private Paint mPaint = new Paint();
    private Path mPath = new Path();
    private Canvas mCanvas;
    private Bitmap mBeforeBitmap;
    private Bitmap mBackBitmap;
    private int mLastX,mLastY;
    private int screenW, screenH; //屏幕宽高
    private Xfermode mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);


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

    public StripMeiZi(Context context, AttributeSet attrs) {
        super(context, attrs);
        screenW = ScreenUtil.getScreenW(context);
        screenH = ScreenUtil.getScreenH(context);
        init();
    }


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

    private void init() {
        //背后图片,这里让它全屏
        mBackBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.meizi_back);
        mBackBitmap = Bitmap.createScaledBitmap(mBackBitmap, screenW, screenH, false);
        //前面的图片,并绘制到Canvas上
        mBeforeBitmap = Bitmap.createBitmap(screenW, screenH, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBeforeBitmap);
        mCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(),
                R.mipmap.meizi_before), null, new RectF(0, 0, screenW, screenH), null);
        //画笔相关的设置
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND); // 圆角
        mPaint.setStrokeCap(Paint.Cap.ROUND); // 圆角
        mPaint.setStrokeWidth(80);    // 设置画笔宽
    }

    private void drawPath() {
        mPaint.setXfermode(mXfermode);
        mCanvas.drawPath(mPath, mPaint);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (action)
        {
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mPath.moveTo(mLastX, mLastY);
                break;
            case MotionEvent.ACTION_MOVE:

                int dx = Math.abs(x - mLastX);
                int dy = Math.abs(y - mLastY);

                if (dx > 3 || dy > 3)
                    mPath.lineTo(x, y);

                mLastX = x;
                mLastY = y;
                break;
        }
        invalidate();
        return true;
    }
}

Layout codeactivity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.jay.xfermodedemo2.StripMeiZi
        android:layout_width="match_parent"
        android:layout_height="match_parent"/></RelativeLayout>

3. Code sample download:

XfermodeDemo2.zip


Summary of this section:

Okay, in this section we wrote another version of Xfermode and PorterDuff Practical example - Demo of tearing off beautiful woman's clothes, compared to what we have done before The way of tearing off the clothes of a beautiful woman (making the 20*20 pixels near the touch point transparent) is much more elegant~ The code is also much simpler, right? Do you have it? Realize the benefits that Android image shuffling Xfermode brings to us, or the importance of custom controls! Well, what are you waiting for? Open your IDE, play through the code, and taste the pleasure of tearing off beautiful women's clothes~6.gif