preface

Google I/O 2014 released Material Design, and I have used it recently, which gives me a feeling that it is simple and gorgeous. In Material Design, I want to use Ripple effect most, and today I will take you to work on this effect!

rendering

TODO

First of all, we need to get the position of the click, then draw a circle (or other shapes) in the View with the click position as the center, and then draw text.

The code is as follows:

public class SpecialEffectsButton extends View {

    private Context context;
    private Paint textPaint;
    private boolean isfollow = false;
    private Paint bgPaint;
    private int radius = -1;
    private int height;
    private int width;
    private int time = 1;
    private float centerX;
    private float centerY;

    public SpecialEffectsButton(Context context) {
        super(context);
        init(context);
    }


    public SpecialEffectsButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public SpecialEffectsButton(Context context, AttributeSet attrs,   int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }
Copy the code

The first step is to get the position of our click (coordinate XY), we need to define a constant to store the position of XY, of course we can also use Point and PointF, both are ok, my personal preference is constant, then draw the circle, the center of the circle we declare and then declare a radius, Then there is the click state (isfollow, whether to follow, enter to imitate a little bit more), and finally there is the declaration of the width and height of the View.


  private void init(Context context) {

        this.context = context;

        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setStyle(Paint.Style.STROKE);
        textPaint.setColor(Color.parseColor("#ECFAF2"));
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setTextSize(50);
        textPaint.setTypeface(Typeface.DEFAULT_BOLD);
        bgPaint = new Paint();
        bgPaint.setAntiAlias(true);
        bgPaint.setStyle(Paint.Style.FILL);
        bgPaint.setColor(Color.parseColor("#B7B7B7"));

    }Copy the code

So in init(Context Context), we initialize a brush, Paint is a brush, and it’s very important for drawing, the brush basically holds the color, the style, and so forth, and it specifies how to draw text and shapes, and there are a lot of ways to set brush objects, and basically there are two types of brush objects, One has to do with drawing graphics, and one has to do with drawing text. Here I’ll just describe the method we used this time, and the rest will be explained in another article.

methods Introduction to the
setAntiAlias(boolean) Enable anti-aliasing (it will not be smooth if not enabled)
setStyle(Paint.Style.STROKE) Set the brush style to FILL, FILL_OR_STROKE, or STROKE
setColor(Color) Set the brush color
setTextAlign(Paint.Align.CENTER) Is to achieve horizontal center
setTextSize(int size) Setting text size
setTypeface Set font, DEFAULT regular font, DEFAULT_BOLD bold type, etc
private boolean isValidClick(float x, float y) { if (x >= 0 && x <= getWidth() && y >= 0 && y <= getHeight()) { return true; } return false; } public boolean onInterceptTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_UP: return true; } return false; } public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (! isValidClick(event.getX(), event.getY())) { return false; } return true; case MotionEvent.ACTION_UP: if (! isValidClick(event.getX(), event.getY())) { return false; } centerX = event.getX(); centerY = event.getY(); isfollow = ! isfollow; timerHandler.sendEmptyMessageDelayed(time, time); return true; } return false; }Copy the code

So we need to rewrite onInterceptTouchEvent and onTouchEvent to get the location of the click. We also need to consider whether the location of the click is valid. IsValidClick is used to check whether the control is valid. It is within the width and height of the control. If it’s a valid event, then we record the position of the touch and change our state, and then we send a Handler where we redraw the button and increment the radius of the circle each time.

methods Introduction to the
onTouchEvent Trigger touch event
onInterceptTouchEvent Triggers the intercept touch event
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); height = getMeasuredHeight(); width = getMeasuredWidth(); Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); float fontHeight = fontMetrics.bottom - fontMetrics.top; float textBaseY = height - (height - fontHeight) / 2 - fontMetrics.bottom; canvas.drawColor(isfollow == true ? Color.parseColor("#00CE7E") : Color.parseColor("#B7B7B7")); bgPaint.setColor(isfollow == true ? Color.parseColor("#B7B7B7") : Color.parseColor("#00CE7E")); canvas.drawCircle(centerX, centerY, radius, bgPaint); canvas.drawText(isfollow == true ? "Cancel" : "focus ", width / 2, textBaseY, textPaint); }Copy the code

First, the onDraw method. The three main methods in View are OnMeasure(), onDraw() and onLayout(). Things like TextView and Button are drawn inside onDraw. We initialized a brush at the beginning, so what do we need when we have a brush? Yeah, it’s Canvas, Canvas is our Canvas, and Paint and I’m just going to talk about the method that we use, and I’ll talk about the other methods in a future article.

methods Introduction to the
drawColor(Color) Painting a canvas with color can also be interpreted as a canvas with color
drawCircle(x,y,radius,paint) Draw a circle with the following parameters: X-axis, Y-axis (center), radius and brush
drawText(String, x, y, paint)) To draw text, the parameters are: the text to draw, the xy coordinates of the position and the brush
Handler timerHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); radius += 10; if (radius <= width * 2) { timerHandler.sendEmptyMessageDelayed(time, time); invalidate(); } else { radius = -1; }}};Copy the code

Finally, there is our Hanlder, whose main function is to send a message to update our view, so as to achieve the ripple effect (we can also use custom animation to achieve this), here we determine whether the radius is greater than 2 times the width of our view, if it is larger, we stop repainting, otherwise the radius +10 repainting each time, In this case, the invalidate() method, calling the invalidate method will cause our View to redraw, so it will go onDraw every time we call it, and remember that the invalidate can only be called in the UI thread, If you want to flush back the View from a non-UI thread, you call the postInvalidate() method.

Go ahead and create your own cool View!