Android自定義View——實現水波紋效果類似剩餘流量球
阿新 • • 發佈:2019-01-11
最近突然手癢就想搞個貝塞爾曲線做個水波紋效果玩玩,終於功夫不負有心人最後實現了想要的效果,一起來看下吧:
效果圖鎮樓
一:先一步一步來分解一下實現的過程
- 需要繪製一個正弦曲線(sin)或者餘弦曲線(cos)
- 通過水平平移曲線來的到像水波波動的效果
- 水平移動的同時還需要有水位上漲,也就是向上平移
- 裁剪畫布為圓形,在圓形區域繪製曲線
- 通過上面4步就可以實現了
二:現在就來實現第一步,繪製一個sin曲線;這裡畫了一張圖來幫助理解,在PhotoShop中我們繪製一個貝塞爾曲線可以清楚的看到它的控制點如圖:
繪製貝塞爾曲線我們必須要知道三個點:起點、控制點、終點;有了這三個點我們就可以繪製一段簡單二階貝塞爾曲線。從圖中我們可以看出 起點 控制點p1 x1 這三個點繪製了一段曲線,也就是通過path.quadTo()函式新增一個曲線路徑。
假設我們需要繪製一個週期的sin曲線,那麼我們就只需要知道起點、一個週期的寬度、振幅;就可以繪製一個sin曲線了。
三:下面就來看下程式碼的實際操作了,這裡就直接省略掉一些畫筆初始化的操作了可以點選這裡檢視原始碼
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取view的寬度
width = getViewSize(800, widthMeasureSpec);
//獲取view的高度
height = getViewSize(400, heightMeasureSpec);
//獲取起點座標
startPoint = new Point(0, height / 2);
}
首先肯定是要獲取到畫布的大小才能確定好起點的座標,有了起點座標就可以開始繪製我們的曲線了
在ondraw()函式中進行曲線的繪製
/*sin曲線 1/4個週期的寬度*/
private int cycle = 200;
/*sin曲線振幅的高度*/
private int waveHeight = 200;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
path.moveTo(startPoint.x, startPoint.y);
int j = 1;
//迴圈繪製正弦曲線 迴圈一次半個週期
for (int i = 1; i <= 8; i++) {
if (i % 2 == 0) {
//波峰
path.quadTo(startPoint.x + (cycle * j), startPoint.y + waveHeight,
startPoint.x + (cycle * 2) * i, startPoint.y);
} else {
//波谷
path.quadTo(startPoint.x + (cycle * j), startPoint.y - waveHeight,
startPoint.x + (cycle * 2) * i, startPoint.y);
}
j += 2;
}
//繪製封閉的曲線
path.lineTo(width, height);//右下角
path.lineTo(startPoint.x, height);//左下角
path.lineTo(startPoint.x, startPoint.y);//起點
path.close();
canvas.drawPath(path, paint);
}
經過上面一系例操作一個水波紋效果就出來啦
四:接下來就是水平移動這個曲線了為了移動起來更加好看,我們需要在螢幕外面開始繪製一個週期,如下:
//初始化的時候將起點移至螢幕外一個週期
startPoint = new Point(-cycle * 4, height / 2);
//繼續在ondraw()函式最後追加平移程式碼
//判斷是不是平移完了一個週期
if (startPoint.x + 40 >= 0) {
//滿了一個週期則恢復預設起點繼續平移
startPoint.x = -cycle * 4;
}
//每次波形的平移量 40
startPoint.x += 40;
postInvalidateDelayed(150);
path.reset();
只需要這樣就可以產生水波效果了,一起來看效果圖吧。
五:接下來就是要將畫布變成圓形了(其實還是個矩形,只是繪畫區域在你所裁剪的區域),然後在裡面實現水波紋就哦的K了;完整的繪製程式碼如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//裁剪畫布為圓形
Path circlePath = new Path();
circlePath.addCircle(width / 2, height / 2, width / 2, Path.Direction.CW);
canvas.clipPath(circlePath);
canvas.drawPaint(circlePaint);
canvas.drawCircle(width / 2, height / 2, width / 2, circlePaint);
//以下操作都是在這個圓形畫布中操作
//根據進度改變起點座標的y值
startPoint.y = (int) (height - (progress / 100.0 * height));
//起點
path.moveTo(startPoint.x, startPoint.y);
int j = 1;
//迴圈繪製正弦曲線 迴圈一次半個週期
for (int i = 1; i <= 8; i++) {
if (i % 2 == 0) {
path.quadTo(startPoint.x + (cycle * j), startPoint.y + waveHeight,
startPoint.x + (cycle * 2) * i, startPoint.y);
} else {
path.quadTo(startPoint.x + (cycle * j), startPoint.y - waveHeight,
startPoint.x + (cycle * 2) * i, startPoint.y);
}
j += 2;
}
//繪製封閉的曲線
path.lineTo(width, height);//右下角
path.lineTo(startPoint.x, height);//左下角
path.lineTo(startPoint.x, startPoint.y);//起點
path.close();
canvas.drawPath(path, paint);
drawText(canvas, textPaint, progress + "%");
//判斷是不是平移完了一個週期
if (startPoint.x + 40 >= 0) {
//滿了一個週期則恢復預設起點繼續平移
startPoint.x = -cycle * 4;
}
//每次波形的平移量 40
startPoint.x += 40;
if (autoIncrement) {
if (progress >= 100) {
progress = 0;
} else {
progress++;
}
}
postInvalidateDelayed(150);
path.reset();
}