效果圖:
關於貝塞爾曲線的介紹可以參照文章
https://www.cnblogs.com/wjtaigwh/p/6647114.html
瞭解完貝塞爾曲線就來完成波浪自定義View
大體思路就是先繪製一條超出屏幕長的二階貝塞爾曲線,然後再動畫循環平移曲線。
package com.xingyun.waveapplication;
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.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
/**
* Created by SY on 2018/5/1.
*/
public class WaveView1 extends View {
private int width = 0;
private int height = 0;
private int baseHeight = 0;// 波浪高度
private int waveHeight = 200;// 波峯波、波谷高度
private int waveWidth;//波浪寬度
private float offset = 0;//偏移量
private Paint paint;
public WaveView1(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}
/**
* 設置開啓動畫
*/
private void startAni() {
ValueAnimator animator = ValueAnimator.ofFloat(0, waveWidth);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatorValue = (float) animation.getAnimatedValue();
offset = animatorValue;
postInvalidate();
}
});
animator.setDuration(1000);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(getPath(), paint);
}
//初始化paint
private void initPaint() {
paint = new Paint();
paint.setColor(Color.parseColor("#88FFFFFF"));
paint.setStyle(Paint.Style.FILL);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
width = getMeasuredWidth();
height = getMeasuredHeight();
waveWidth = width;
baseHeight = height / 2;
startAni();
}
/**
* 生成path
*
* @return
*/
private Path getPath() {
int itemWidth = waveWidth / 2;//波長
Path path = new Path();
path.moveTo(-itemWidth * 3 , baseHeight);//起始點座標
for (int count = -3; count < 2; count++) {
int controlX = count * itemWidth;
path.quadTo(controlX + itemWidth / 2 + offset,//控制點的X
getWaveHeight(count),//控制點的Y
controlX + itemWidth + offset,//結束點的X
baseHeight//結束點的Y
);
}
//封閉區域
path.lineTo(width, height);
path.lineTo(0, height);
path.close();
return path;
}
//計算波峯、波谷
private int getWaveHeight(int num) {
if (num % 2 == 0) {
return baseHeight - waveHeight;
}
return baseHeight + waveHeight;
}
}
初始化畫筆之後再onLayout方法獲取寬高,然後ondraw方法裏繪製關鍵的曲線。通過getPath方法得到曲線的path。通過path.moveTo方法設置起始點座標,調用path.quadTo繪製曲線,quadTo四個參數分別爲控制點的XY座標和結束點的XY座標。完成後調用path.lineTo連接屏幕底部,將區域封閉起來。其中控制點Y座標爲一上一下交替,通過 getWaveHeight方法判斷是波峯還是波谷。這樣波浪曲線就繪製完成了。最後只要設置動畫不斷重繪並且循環動畫就完成了。