Water ripple effect of custom control
foreword
Recently, I spent nearly a week reading "The Art of Android Development" by Ren Yugang. This book is very suitable for children's boots in the advanced stage. Because the positioning is an advanced book, many basic knowledge is taken in one stroke. In the book, I learned a lot of details and knowledge points that were ignored before. Next, I want to write a custom control for the water ripple effect, so I can practice it.
Before writing, you have to understand the Bezier curve. The Bezier curve is really an interesting thing. Here I recommend a link to the Android custom control-Path's Bezier curve and gesture track, water ripple effect . In fact, this water ripple effect is still It is very easy to implement, as long as you know some of these methods and use them flexibly (the use of Path class and animation effects).
First of all, let’s talk about the idea. We need to draw two complete wavy lines (one on the screen and one off the screen, in order to look good), then set a variable value for the animation, and keep redrawing the waves according to this value, Then this animation appeared.
Let's start the formal code steps. We write a MyWaveView to inherit View. The member variables need a brush Paint, path Path, and wavelength mWaveLength (for the beauty of the wavy line, I simply tested a variety of wavelengths, and I feel that only the complete wavelength is just right When equal to the width of the screen, the wavy line jitter looks perfect, here mWaveLength=800 pixels), as well as the variable dx and the distance from the top of the ripple mCustomY. Initialize Path and brush Paint in the construction method to set properties such as color, style, etc. The relatively important code starts below. We need to move the starting point to the starting point of the first wave on the left side of the screen
mPath.moveTo(-mWaveLength + dx, mCustomY);
and then draw two wavy lines in the for loop ( The method starting with r in Path and the method without r actually have the same effect, but the one starting with r takes the current starting point as the coordinate origin, and the one without r takes the coordinate origin as the starting point), here I am used to using r The method at the beginning, use the second-order Bezier curve Path.rQuadTo(float dx1, float dy1, float dx2, float dy2) to draw two continuous waves, andmPath.rQuadTo(halfWaveLen / 2, -100, halfWaveLen, 0);mPath.rQuadTo(halfWaveLen / 2, 100, halfWaveLen, 0);
then cover the entire screen `mPath.lineTo(getWidth(), getHeight ());mPath.lineTo(0, getHeight());mPath.close();canvas.drawPath(mPath, mPaint);- Finally, write a method to start the animation to achieve the effect
public void startAnim() {
ValueAnimator animator = ValueAnimator.ofInt(0, mWaveLength);
animator.setDuration(1500);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
dx = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
- renderings
- Finally attach the complete code
public class MyWaveView extends View {
private static final String TAG = "MyView";
private Paint mPaint;
private Path mPath;
private int mWaveLength = 800;
private int dx;
private int mCustomY;
public MyWaveView(Context context, AttributeSet attrs) {
super(context, attrs);
mPath = new Path();
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
mCustomY = 400;
int halfWaveLen = mWaveLength / 2;
mPath.moveTo(-mWaveLength + dx, mCustomY);
for (int i = -mWaveLength; i <= getWidth() + mWaveLength; i += mWaveLength) {
Log.i(TAG, "onDraw: "+i);
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);
}
public void startAnim() {
ValueAnimator animator = ValueAnimator.ofInt(0, mWaveLength);
animator.setDuration(1500);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
dx = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
}