自定義View,貝賽爾曲線實現水波紋進度條
最終的效果:
思路就是在onDraw()中畫一些內容,主要方法有這些:
/**
* 剪裁圓形區域
*/
clipCircle(canvas);
/**
* 畫圓邊線
*/
drawCircle(canvas);
/**
* 畫波浪線
*/
drawWave(canvas);
/**
* 畫進度文字
*/
drawText(canvas);
1.clipCircle(canvas)這個方法剪裁出一個圓形畫布:
private void clipCircle(Canvas canvas) { Path circlePath = new Path(); circlePath.addCircle(screenWidth / 2, screenHeignt / 2, screenHeignt / 2, Path.Direction.CW); canvas.clipPath(circlePath); }
2.drawCircle(canvas)畫出圓的邊線:
private void drawCircle(Canvas canvas) {
canvas.drawCircle(screenHeignt / 2, screenHeignt / 2, screenHeignt / 2, circlePaint);
}
3.drawWave(canvas)畫波浪線:
private void drawWave(Canvas canvas) { int height = (int) (progress / 100 * screenHeignt); startPoint.y = -height; canvas.translate(0, screenHeignt); path = new Path(); wavePaint.setStyle(Paint.Style.FILL); wavePaint.setColor(Color.GREEN); int wave = screenWidth / 4; path.moveTo(startPoint.x, startPoint.y); for (int i = 0; i < 4; i++) { int startX = startPoint.x + i * wave * 2; int endX = startX + 2 * wave; if (i % 2 == 0) { path.quadTo((startX + endX) / 2, startPoint.y + amplitude, endX, startPoint.y); } else { path.quadTo((startX + endX) / 2, startPoint.y - amplitude, endX, startPoint.y); } } path.lineTo(screenWidth, screenHeignt / 2); path.lineTo(-screenWidth, screenHeignt / 2); path.lineTo(-screenWidth, 0); path.close(); canvas.drawPath(path, wavePaint); startPoint.x += 10; if (startPoint.x > 0) { startPoint.x = -screenWidth; } path.reset(); }
這段程式碼說來有些複雜;
首先我們看到height,這個是波浪曲線所在的y軸的曲線,progress是傳進來的進度為了計算我轉化成百分比先除以100再乘以螢幕高度(這裡的螢幕高度大家可以理解成直徑)。
其次我把螢幕y軸移動到了控制元件的底部也就是如圖這樣的:
接下來我就開始建立path設定畫筆開始畫波浪線了, 我們設定的startpoint為x軸負一個螢幕的寬度,從這個點開始畫兩個完整的正弦曲線,wave的大小為screenWidth/4(在圖中標註了),在for中改變startpoint並且畫出曲線(每個人可能有不一樣的實現方法,這段程式碼看起來可能費勁),最終把這個波浪與右下做下點連起來形成閉合圖形,用於著色,那麼畫完了怎麼讓波浪凍起來呢?我們每次重新整理ondraw()都讓起始點的x+10,然後判斷如果x大於0,也就是起始點來到了(0,screenWidth)這個點的時候,我們把startpoint的x座標回到最初的位置即可,這樣每次重新整理就形成了一個動態波浪的效果了。波浪的高度通過剛開始的startPoint.y = -height來實現,加符號因為y軸複方向為進度的增長方向。
4.drawText()繪製文字:
private void drawText(Canvas canvas) {
Rect targetRect = new Rect(0, -screenHeignt, screenWidth, 0);
Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(String.valueOf((int) textProgress + "%"), targetRect.centerX(), baseline, textPaint);
}
繪製文字為了讓文字居中我做了一些必要的計算
PS:細心的小夥伴在setProgress()這個方法中發現了一點問題為什麼會這麼寫呢:
public void setProgress(float progress) {
this.textProgress = progress;
if (progress == 100) {
this.progress = progress + amplitude;
} else {
this.progress = progress;
}
}
原因是因為當我們的進度達到100%的時候,由於sin曲線和從左到右的波動還是會出現空白流動的區域,就像如圖這樣:
這樣豈不是很蛋疼,進度滿了不能出現這種情況,所以我手動判斷如果進度在100%就把y座標加上一個保險值,保證不出現以上這個情況。
書寫本文旨在練習與理解自定義view、貝賽爾曲線等等知識,如有錯誤歡迎指正,原始碼連結GitHub傳送門,歡迎clone&start