效果图
贝塞尔曲线
我们使用到的是Path类的quadTo(x1, y1, x2, y2)方法,属于二阶贝塞尔曲线,使用一张图来展示二阶贝塞尔曲线,这里的(x1,y1)是控制点,(x2,y2)是终止点,起始点默认是Path的起始点(0,0)
原理分析
1、通过for循环画出两个波纹,需要波纹的-mWL点、-3/4 * mWL点、-1/2 * mWL、-1/4 * mWL四个点,通过path的quadTo画出
2、接着通过ValueAnimator对offset递增,实现平移效果,并无限重复
实现一次循环波纹,红点为贝塞尔坐标:
实现无限次循环波纹:
接下来在波纹下方的空白处画上一个矩形:
实现步骤
1、初始化变量
//波浪画笔
private Paint mPaint;
//测试红点画笔
private Paint mCyclePaint;
//波浪Path类
private Path mPath;
//一个波浪长度
private int mWaveLength = 1000;
//波纹个数
private int mWaveCount;
//平移偏移量
private int mOffset;
//波纹的中间轴
private int mCenterY;
//屏幕高度
private int mScreenHeight;
//屏幕宽度
private int mScreenWidth;
2、初始化画笔
public WaveView(Context context, AttributeSet attrs) {
super(context, attrs);
mPath = new Path();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.LTGRAY);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
setOnClickListener(this);
//用来绘制测试红点
mCyclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCyclePaint.setColor(Color.RED);
mCyclePaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
3、获取宽和高
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mScreenHeight = h;
mScreenWidth = w;
//加1.5:至少保证波纹有2个,至少2个才能实现平移效果
mWaveCount = (int) Math.round(mScreenWidth / mWaveLength + 1.5);
mCenterY = mScreenHeight / 2;
}
4、绘制水波纹
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
//移到屏幕外最左边
mPath.moveTo(-mWaveLength + mOffset, mCenterY);
for (int i = 0; i < mWaveCount; i++) {
//正弦曲线
mPath.quadTo((-mWaveLength * 3 / 4) + (i * mWaveLength) + mOffset, mCenterY + 60, (-mWaveLength / 2) + (i * mWaveLength) + mOffset, mCenterY);
mPath.quadTo((-mWaveLength / 4) + (i * mWaveLength) + mOffset, mCenterY - 60, i * mWaveLength + mOffset, mCenterY);
//贝塞尔坐标,测试红点
canvas.drawCircle((-mWaveLength * 3 / 4) + (i * mWaveLength) + mOffset, mCenterY + 60, 5, mCyclePaint);
canvas.drawCircle((-mWaveLength / 2) + (i * mWaveLength) + mOffset, mCenterY, 5, mCyclePaint);
canvas.drawCircle((-mWaveLength / 4) + (i * mWaveLength) + mOffset, mCenterY - 60, 5, mCyclePaint);
canvas.drawCircle(i * mWaveLength + mOffset, mCenterY, 5, mCyclePaint);
}
//填充矩形
mPath.lineTo(mScreenWidth, mScreenHeight);
mPath.lineTo(0, mScreenHeight);
mPath.close();
canvas.drawPath(mPath, mPaint);
}
5、实现平移效果
@Override
public void onClick(View view) {
ValueAnimator animator = ValueAnimator.ofInt(0, mWaveLength);
animator.setDuration(1000);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mOffset = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
6、源码下载