原理图:
图中的矩形为手机屏幕。
通过属性动画类ValueAnimator不断改变点0的横坐标,随着点0横坐标向右移动,点1,点2,点3,点4,点5,以及四个控制点的坐标随着点0的移动同时位移相同距离,每一次坐标点更新,我们调用一次invalidate()方法,调用draw重新绘制视图,绘制四段贝塞尔曲线。最后点0移动一个波长到原先点2的位置,这样就完成了一次动画。
这样,通过循环不断的动画效果,我们就实现了波浪的效果。
参考:
android贝塞尔曲线之波浪效果
Android贝塞尔曲线————波浪效果(大波浪)
Android-贝塞尔曲线
自定义view1:
package com.android.imooc;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
public class MyWaveView extends View {
private Paint paint;
//view 的宽高
private int mWidth;//等于一个波长
private int mHeight;
//浪
private int waveHeight = 100;// 波幅
//偏移
private float offset = 0f;//偏移量
private int baseLine = 0;// 基线,用于控制水位上涨下落的
private Path mPath;
public MyWaveView(Context context) {
this(context, null);
}
public MyWaveView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyWaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public MyWaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
mPath = new Path();//构造方法中创建对象
paint = new Paint();
paint.setDither(true);
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);//填充
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
baseLine = mHeight / 2;//view的一半高度为基线
Log.e("111", "===onSizeChanged===");
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.e("111", "===onLayout===");
ValueAnimator mAnimator = ValueAnimator.ofFloat(0, mWidth);//移动一个波长
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
offset = (float) animation.getAnimatedValue();//不断的设置偏移量,并重画
postInvalidate();
}
});
mAnimator.setDuration(2000);
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.e("111", "===onDraw===");
mPath.reset();//重置path
mPath.moveTo(-mWidth / 2 * 3, baseLine);//起始坐标
//关键部分
for (int i = -3; i < 2; i++) {
int startX = i * mWidth / 2;
//二阶贝塞尔曲线
mPath.quadTo(startX + mWidth / 4 + offset,//控制点的X
getWaveHeight(i),//控制点的Y
startX + mWidth / 2 + offset,//结束点的X
baseLine//结束点的Y
);
}
mPath.lineTo(mWidth, mHeight);
mPath.lineTo(0, mHeight);
mPath.close();
canvas.drawPath(mPath, paint);
//参考线
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
canvas.drawLine(0, mHeight / 2, mWidth, mHeight / 2, paint);
paint.setColor(Color.BLUE);
}
private int getWaveHeight(int num) {
if (num % 2 == 0) {
return baseLine + waveHeight;
}
return baseLine - waveHeight;
}
}
自定义view2:
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
public class MyWaveViewI extends View {
private Paint paint;
//view 的宽高
private int mWidth;//等于一个波长
private int mHeight;
//波幅
private float mWaveHeight = 100f;
private int baseLine = 0;// 基线,用于控制水位上涨下落的
private float offset;
private PointF startP;
private PointF p0, p1, p2, p3, p4, p5;
private PointF ctrlP0, ctrlP1, ctrlP2, ctrlP3, ctrlP4;
public MyWaveViewI(Context context) {
this(context, null);
}
public MyWaveViewI(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyWaveViewI(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public MyWaveViewI(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
paint = new Paint();
paint.setDither(true);//抗抖动
paint.setAntiAlias(true);//抗锯齿
paint.setColor(Color.RED);//色值
paint.setStyle(Paint.Style.FILL);//填充
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.e("111", "===onSizeChanged===");
mWidth = w;
mHeight = h;
//view的一半高度为基线
baseLine = mHeight / 2;
//起点
startP = new PointF(-mWidth * 3 / 2, baseLine);
p0 = new PointF(-mWidth * 3 / 2, baseLine);
p1 = new PointF(-mWidth, baseLine);
p2 = new PointF(-mWidth / 2f, baseLine);
p3 = new PointF(0, baseLine);
p4 = new PointF(mWidth / 2f, baseLine);
p5 = new PointF(mWidth, baseLine);
//贝塞尔曲线控制点
ctrlP0 = new PointF(-mWidth * 5 / 4f, baseLine + mWaveHeight);
ctrlP1 = new PointF(-mWidth * 3 / 4f, baseLine - mWaveHeight);
ctrlP2 = new PointF(-mWidth / 4f, baseLine + mWaveHeight);
ctrlP3 = new PointF(mWidth / 4f, baseLine - mWaveHeight);
ctrlP4 = new PointF(mWidth * 3 / 4f, baseLine + mWaveHeight);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.e("111", "===onLayout===");
ValueAnimator mAnimator = ValueAnimator.ofFloat(0, mWidth);//移动一个波长
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
offset = (float) animation.getAnimatedValue();
postInvalidate();
}
});
mAnimator.setDuration(2000);
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.e("111", "===onDraw===");
Path mPath = new Path();
mPath.moveTo(startP.x, startP.y);
mPath.quadTo(ctrlP0.x + offset, ctrlP0.y, p1.x + offset, p1.y);
mPath.quadTo(ctrlP1.x + offset, ctrlP1.y, p2.x + offset, p2.y);
mPath.quadTo(ctrlP2.x + offset, ctrlP2.y, p3.x + offset, p3.y);
mPath.quadTo(ctrlP3.x + offset, ctrlP3.y, p4.x + offset, p4.y);
mPath.quadTo(ctrlP4.x + offset, ctrlP4.y, p5.x + offset, p5.y);
mPath.lineTo(p5.x, mHeight);
mPath.lineTo(p3.x, mHeight);
mPath.lineTo(p3.x, p3.y);
mPath.close();
canvas.drawPath(mPath, paint);
//参考线
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10);
canvas.drawLine(0, mHeight / 2, mWidth, mHeight / 2, paint);
paint.setColor(Color.RED);
}
}
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<com.android.imooc.MyWaveView
android:layout_width="240dp"
android:layout_height="240dp"
android:background="#e4e416" />
<View
android:layout_width="match_parent"
android:layout_height="20px"
android:background="#ffffff" />
<com.android.imooc.MyWaveViewI
android:id="@+id/wv1"
android:layout_width="240dp"
android:layout_height="240dp"
android:background="#e4e416" />
</LinearLayout>