Android自定義View之手把手帶你自定義一個進度條
本文首發於公眾號“AntDream”,歡迎微信搜尋“AntDream”或掃描文章底部二維碼關注,和我一起每天進步一點點
自定義View是平時開發過程中逃不過的一劫,效果千變萬化,唯有迎難而上,才能以不變應萬變。

進度條效果示意圖
分析需求
這次碰到一個簡單的需求,就是如上圖的載入進度條。分析總結後就幾條:
- 初始時是一圈虛線
- 進度開始時慢慢繪製實線
- 載入成功時加快實線的繪製,快速繪製完一圈表示成功
分析完需求,那我們就來想想怎麼實現這個特殊的View。
準備工作
首先我們肯定先需要掌握基本的View的繪製,比如畫筆Paint、畫布Canvas,以及View的繪製原理等。
自定義View,其實可以這樣簡單來理解:假設我們自己拿筆去畫這個圖,我們會怎麼畫?會有哪些步驟?然後我們用學到的知識編寫程式讓計算機幫我們畫。所以我們得先自己知道怎麼去畫才行。
這裡我分析一下,可分為這幾步:
- 先畫一圈虛線
- 然後根據進度繪製實線
- 當收到加快進度的訊號後快速繪製完一圈實線
這裡就有幾個點了:
- 怎麼快速繪製出一圈虛線呢?
- 繪製實線怎麼控制繪製的快慢呢?
- 怎麼加快實線的繪製呢?
預備知識:
- ofollow,noindex">Android自定義View之Paint繪製文字和線
- Android自定義View之Canvas
- Android自定義View之invalidate方法和postInvalidate方法
- Android自定義View注意事項
實踐
這裡我們採用繼承View的方式來實現
畫虛線
這裡最簡單的方式就是利用Paint的PathEffect屬性,PathEffect屬性用來控制畫筆Paint的路徑樣式,比如虛線,折線的拐角圓滑等。
虛線對應的PathEffect為DashPathEffect,具體用法如下:
mRimPaint.setPathEffect(new DashPathEffect(new float[]{3f, 30f}, 0));
其中float陣列用來控制虛線的效果,陣列的第一個元素控制虛線的長短,第二個用來控制虛線之間的間隔,具體的效果大家可以動手試一下,這樣子比較直觀。需要注意的是float數組裡面必須至少包含2個元素,並且必須是偶數個元素。
所以我們畫一圈虛線就簡單了:
//虛線畫筆 mRimPaint = new Paint(); mRimPaint.setAntiAlias(true); mRimPaint.setStyle(Paint.Style.STROKE); mRimPaint.setColor(this.mRimColor); mRimPaint.setStrokeWidth(this.mRimWidth); mRimPaint.setStrokeCap(Paint.Cap.ROUND); mRimPaint.setPathEffect(new DashPathEffect(new float[]{3f, 30f}, 0));
其中需要注意的是,setStrokeCap這個屬性。我們設定的 Cap.ROUND 屬效能讓我們繪製出的線段更加圓滑。其他的屬性在預備知識中都有介紹。
準備好畫筆以後我們就可以開始繪製我們的虛線了,繪製當然需要在onDraw方法中
@Override public void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawArc(mCircleBounds, 360.0f, 360.0f, false, mRimPaint); }
這裡我們採用了Canvas的drawArc方法,就是繪製一段360度的圓弧。具體的引數屬性可以參看上面預備知識中關於Canvas的文章。
其中需要注意的是第一個引數,也就是圓弧外框矩形的確定。這個矩形的作用就是確定整個圖形的邊界,因為我們的線段是有寬度的,所以在計算邊界的時候需要考慮到線段的寬度。
實際上我們後面肯定要讓這個進度條的一些屬效能自定義,比如進度條的顏色,寬度等,所以我們在繪製的時候就需要考慮到進度條寬度的問題。
要解決這個問題也簡單,在建立這個外框時,把進度條的寬度考慮進去就行了。具體看程式碼:
float circleWidthHalf = (float) this.mBarWidth / 2.0F > (float) this.mRimWidth / 2.0F ? (float) this.mBarWidth / 2.0F : (float) this.mRimWidth / 2.0F; this.mCircleBounds = new RectF(paddingLeft + circleWidthHalf, paddingTop + circleWidthHalf, (float) width - paddingRight - circleWidthHalf, (float) height - paddingBottom - circleWidthHalf);
其中,mBarWidth是實線進度條的寬度,mRimWidth是虛線的寬度。由於實線和虛線可能寬度不一樣,所以我們需要以較寬的為準。
畫實線進度條
有了以上的基礎,那畫實線進度條就簡單了。同樣是先設定Paint,然後用drawArc方法繪製。
設定Paint屬性
//實線畫筆 mBarPaint = new Paint(); mBarPaint.setAntiAlias(true); mBarPaint.setColor(this.mBarColor); mBarPaint.setStyle(Paint.Style.STROKE); mBarPaint.setStrokeCap(Paint.Cap.ROUND); mBarPaint.setStrokeWidth(this.mBarWidth);
然後用Canvas的drawArc方法繪製
canvas.drawArc(this.mCircleBounds, (float) this.mStartAngle, degrees, false, this.mBarPaint);
這裡繪製實線就需要特殊處理了,因為我們需要慢慢地繪製,而不是一次性的繪製出一個實線圓。這裡的思路也很簡單,就是隨著時間的推移慢慢地繪製實線進度,只不過角度越來越大。
所以要有載入的進度效果,關鍵就是要控制繪製實線的角度了,那這個角度怎麼確定呢?我們又怎麼在角度改變時,實時重新整理我們的View呢?
我們下次見分曉......
歡迎關注我的微信公眾號,和我一起每天進步一點點!

AntDream