I haven’t written in a few days and have been working on customizing the “drop-down refresh” control. I have been reading the source code of SwipeRefreshLayout for several days. I am so confused that I can’t write my demo in half. We’ll look into it when we have time. Today is the weekend, I learned something during the day, went out for some dancing (not square dancing) tonight, now I am writing my blog, I will go to bed soon.

Some people ask why there are so many open source custom views now, and we have to work so hard to build our own wheels. A blogger once said, and I think it’s very well said. ‘There are a lot of open source libraries out there that have a lot of applicability,’ he said. ‘If we use them, you’ll find a lot of features we don’t need and it will make the package bigger.’ It’s hard to maintain code that you didn’t write yourself, and if your boss asks you to change something, you’ll be at a loss.





I had been thinking about how to achieve a scratch-card effect, but my ability was limited, so I kept dragging. This afternoon idle nothing, literally knocked on the code, a run, found a success, I wipe! I was surprised at the time, and I didn’t seem to find it difficult, but it was very difficult before. Anyway, since it’s done, let’s teach you how to achieve this function.

Let’s take a look at the results:




I don’t know why I can’t send the GIF. If you want to see the GIF, you can go to my Github to watch it. The Github address will be given at the end of this article. Of course, if you are a passionate youth, want to personally feel the trial effect of this control, then you can scan the following TWO-DIMENSIONAL code, directly download the Demo to run on the phone. 😁😁😁 Don’t miss it. I highly recommend that you download the Demo, which contains many of the cases I’ve written before and will be updated all the time. Don’t worry about downloading, there is no chicken soup, elders guarantee, are technical dry goods ~ ~ ~




Demo Download Address

The idea is this: we need a background and a foreground, the background is hidden behind the scratch card, may be a picture, may be a million awards, the foreground is gray cover layer. And then we need a brush, and the brush will form a track on top of the foreground, and we’ll just make the track transparent, so we can see what’s underneath.

Without further ado, start coding. First, we define a custom GuaGuaKa class that inherits from View and implements its constructor.

public class GuaGuaKa extends View { private Paint pathPaint; private Path path; private Bitmap cacheBitmap; private Canvas cacheCanvas; private Bitmap backBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.demo); public GuaGuaKa(Context context) { this(context, null); } public GuaGuaKa(Context context, AttributeSet attrs) { super(context, attrs); this.setBackgroundColor(Color.WHITE); pathPaint = new Paint(); pathPaint.setAntiAlias(true); pathPaint.setDither(true); pathPaint.setColor(Color.BLACK); pathPaint.setStyle(Paint.Style.STROKE); pathPaint.setStrokeWidth(50); pathPaint.setStrokeCap(Paint.Cap.ROUND); pathPaint.setStrokeJoin(Paint.Join.ROUND); path = new Path(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); cacheBitmap = Bitmap.createBitmap(getWidth() - getPaddingLeft() - getPaddingRight(), getHeight() - getPaddingTop() - getPaddingBottom(), Bitmap.Config.ARGB_8888); cacheCanvas = new Canvas(cacheBitmap); }}Copy the code

In the constructor, I initialize the paint properties. If you are not familiar with the use of Paint, you can refer to another article called Paint Special effects. . In the onLayout method, I initialize the Bitmap and canvas. I want to ask you, why don’t I just initialize it in the constructor? If you want to know, you can read on. Generally speaking, the initialization of bitmap and Canvas can be placed in the constructor, and I really hope that can be done. Otherwise, a piece of code here and a piece of code there will be more code in the future, and it will not be convenient to find. Ideal is full, reality is very skinny! If you’ve noticed, cacheBitmap is initialized by passing getWidth(), which returns the width of the GuaGuaKa control. This value is not available until after the onMeasure method is executed. Returns 0 until the onMeasure is executed. Why do I know this? Because I learned how to draw a View. If you don’t understand, it’s time to learn the drawing mechanism of a View. Step two, we need to handle the finger swipe event, so we can override the onTouchEvent method. The premise is that you must have learned the Android Touch event distribution mechanism, those who have not, the following code is basically unreadable. Doesn’t it feel weird that a simple custom View has to learn so many Android mechanisms? So let’s take a look at the code

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            pointerX = event.getX();
            pointerY = event.getY();
            path.moveTo(pointerX, pointerY);
            break;
        case MotionEvent.ACTION_MOVE:
            path.lineTo(event.getX(), event.getY());
            pointerX = event.getX();
            pointerY = event.getY();
            break;
    }
    invalidate();
    return true;
}Copy the code

When the finger is pressed for the first time, record the x and y coordinates and move the path to this coordinate. Then change the path as the finger moves, and then redraw it to achieve the effect of real-time display. Some blogs draw paths using path.quadto (). I’m using lineTo, but there’s a difference. But we’re not going to talk about it here, okay. Although I won’t discuss it here, it is still necessary to learn the difference between quadTo and lineTo if you don’t know.

If invalidate is called, then the system will execute onDraw, so I’m curious what code the onDraw method will execute. Look directly at the code:

@override protected void onDraw(Canvas Canvas) {Canvas. DrawBitmap (backBitmap, 0, 0, null); // Use porterduff. Mode to draw a line with DST_OUT as the overlap between the background and path. pathPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); cacheCanvas.drawPath(path, pathPaint); DrawBitmap (cacheBitmap, 0, 0, null); }Copy the code

The notes are clear. Paint the background first, then the foreground. The background is an image, and the foreground is a combination of the gray masking layer and the path. The combination rule is DST_OUT.




To be honest, I thought I should use Xor at first and couldn’t figure out why DstOut was used. I haven’t figured it out yet. Shit…

You need to learn the android event distribution mechanism, the drawing mechanism of View, the use of paint effects, the use of Path, Canvas, etc. Learning Bitmap knowledge is so fucking hard!! I learned it, and it was hard, but when you build something, you feel a sense of accomplishment.

At the end of the day, you need to do it yourself, write the code, and briefly analyze why. If you are really lazy, you can go to my Github and copy the code. The code of this blog is not very complete, and focuses on explaining ideas. The complete code is here: github.com/Elder-Wu/No… If you think I write well, you can click a “like” and pay attention to it. In addition, my wechat public account has been launched, and I will share a high-quality technical article every day. Welcome to check it out. (Official account: Code is also a person)




Technical Exchange Group: 471395156 (Welcome to the pit)