First look at the effect map:


Draw Track
Drawing a finger's track is mainly the onTouchEvent() method of intercepting the View and drawing a path based on the finger's track.There are two ways to do this in path
1,Path.lineTo(x,y) method
public class MovePathView extends View { private Path mPath; private Paint mPaint; //Position of finger press private float startX,startY; public MovePathView(Context context) { super(context); init(); } //Initialization private void init() { mPaint = new Paint(); mPath = new Path(); mPaint.setColor(Color.BLUE); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(4); } public MovePathView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public MovePathView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: startX = event.getX(); startY = event.getY(); //Set Origin mPath.moveTo(startX,startY); break; case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_MOVE: float currX = event.getX(); float currY = event.getY(); //Connection mPath.lineTo(currX,currY); //Refresh view invalidate(); break; } //Return true, consumer events return true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath,mPaint); } //Externally available methods, redrawing public void reset(){ mPath.reset(); invalidate(); } }
There are three things you should know about this:
- View's coordinate system
- Event Distribution for View
- Path's moveTo(), lineTo() methods
2. Use Path.quadTo() Draw a curve
public class MoveQuatoView extends View { private Paint mPaint; private Path mPath; //Last position private float mPreX,mPreY; //End position private float endY,endX; public MoveQuatoView(Context context) { super(context); init(); } public MoveQuatoView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public MoveQuatoView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } //Initialization private void init() { mPath = new Path(); mPaint = new Paint(); mPaint.setColor(Color.RED); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(5); mPaint.setAntiAlias(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath,mPaint); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mPath.moveTo(event.getX(), event.getY()); mPreX = event.getX(); mPreY = event.getY(); break; case MotionEvent.ACTION_MOVE: endX = (mPreX + event.getX()) / 2; endY = (mPreY + event.getY()) / 2; mPath.quadTo(mPreX, mPreY, endX, endY); mPreX = event.getX(); mPreY = event.getY(); invalidate(); break; } return true; } }
For the sake of smoothing, endX and endY take only the middle part of the line.
xRipple
Water ripple is mainly used for Path.rQuadTo() method.
rQuadTo() is also a method of drawing curves.

@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStrokeWidth(5); paint.setStyle(Paint.Style.STROKE); paint.setAntiAlias(true); Path path = new Path(); path.moveTo(100,300); /** rQuadTo(float dx1, float dy1, float dx2, float dy2) dx1:The control point X coordinate, which represents the displacement coordinate relative to the last end point X coordinate, can be negative, positive values can be added, and negative values can be subtracted. dy1:The Y coordinate of the control point, the displacement coordinate relative to the Y coordinate of the last endpoint.Negative values can also be used, positive values mean addition, negative values mean subtraction. dx2:The X coordinate of the end point is also a relative coordinate. The displacement value of the X coordinate of the last end point can be negative, positive values can be added, and negative values can be subtracted. dy2:The Y coordinate of the end point is also a relative displacement value relative to the Y coordinate of the last end point.They can be negative, positive values are additive, and negative values are subtractive. */ path.rQuadTo(100,-100,200,0); path.rQuadTo(100,100,200,0); canvas.drawPath(path,paint); }
The code above has two total rQuadTo() methods.
FirstPath.rQuadTo(100, -100,200,0); Start point: (100,300) Control point coordinates: (200,200), X:200=100+100, Y:200=300-100 End point coordinates: (300,300), X:300=100+200, Y:300=300+0 The effect is:![image2.png] (image2.png]Http://upload-images.jianshu.io/upload_Images/2729169-8a82e6e36cd5cf8b.png? ImageMogr2/auto-orient/strip%7CimageView2/2/w/1240) SecondPath.rQuadTo(100,100,200,0);`
The coordinate of the starting point is also the coordinate of the first ending point, so
Starting point coordinates: (300,300)
Control point coordinates: (400,400), X:400 = 300+100, Y:400 = 300+100
End point coordinates: (500,300), X:500 = 300+200, Y:300 = 300+0
Similarly, if there is a thirdPath.rQuadToThen the third starting point is the last ending point (500,300)
Get it clearPath.rQuadToThe use of the () method can achieve the effect of water ripple.
public class RippleView extends View { private Paint mPaint; private Path mPath; //Width of ripple private int mItemWaveLength = 1000; //Distance of each movement of the ripple private int dx; public RippleView(Context context) { super(context); init(); } public RippleView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public RippleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } //Initialization private void init(){ mPath = new Path(); mPaint = new Paint(); mPaint.setColor(Color.RED); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); mPaint.setStrokeWidth(5); mPaint.setAntiAlias(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //After moving, reset mPath and empty the previous path mPath.reset(); //Height from top int originY = 600; //General ripple width int halfWaveLen = mItemWaveLength/2; //With refresh, move dx distance each time mPath.moveTo(-mItemWaveLength+dx,originY); //for loops through all the ripples on the current screen for (int i = -mItemWaveLength;i<=getWidth()+mItemWaveLength;i+=mItemWaveLength){ mPath.rQuadTo(halfWaveLen/2,-100,halfWaveLen,0); mPath.rQuadTo(halfWaveLen/2,100,halfWaveLen,0); } mPath.lineTo(getWidth(),getHeight()); mPath.lineTo(0,getHeight()); mPath.close(); canvas.drawPath(mPath,mPaint); } /** * The purpose of the animation is to move the ripple * Using calls inPath.moveToWhen you move the starting point to the right, you can move it. * And as long as we move the length of a wavelength, the ripple will coincide and an infinite cycle will be possible */ public void startAnim(){ //Animation Move Distance 0~mItemWaveLength ValueAnimator animator = ValueAnimator.ofInt(0,mItemWaveLength); //time animator.setDuration(2000); //Repeats, here is infinite animator.setRepeatCount(ValueAnimator.INFINITE); animator.setInterpolator(new LinearInterpolator()); //Animation Refresh Listening animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //Distance per move dx = (int)animation.getAnimatedValue(); //Refresh View postInvalidate(); } }); animator.start(); } }
This achieves a ripple effect.
Reference:
Graphics of Custom Control Trilogy (6) - Bessel Curve and Gesture Track, Water Wave Effect of Path