自定義View——IndicatorView,源碼地址:ttdevs github
0x00 main
進步、創新都是在不斷變化的需求中誕生的。
-- By ttdevs
這不,新的需求又來了。廢話不多說,先上設計稿:
簡單分析上圖包括三部分:最上面的類Progressbar,中間兩個指示盤和最下面的指示盤。第一個類Progressbar我們項目之前有實現過,但是和這個需求有一些差異,因此決定重新實現一遍。另外兩個圓形指示盤本想通過在一張背景圖片上放一張指針圖,控制指針圖的旋轉來實現,但是考慮到這樣不夠靈活,因此也決定自己來畫。So,我們接下來實現這三個View。
正式開始之前,先看看我們最終效果圖(當然,指針是可以動的):
0x01 分析
-
LineIndicator (第一個,類Progressbar)
主要包括三部分:左側的提示和內容,右側的提示和內容,中間的類Progressbar。兩側的文字比較容易處理,掌握了文字的基本繪制,畫起來是很容易的。中間的類Progressbar由于打算自己畫,所以不會直接貼個Progressbar在上面。我的思路是畫一條長的直線作為背景,中間的指示也是直線,比背景直線粗,直線的Paint設置
StrokeCap
為Paint.Cap.ROUND
即paint.setStrokeCap(Paint.Cap.ROUND);
,這樣就可以有兩頭半圓的效果;中間的指示數字直接畫在粗的指示直線上面即可。這里可能遇到的問題有下面兩個:-
StrokeCap
設置 為Paint.Cap.ROUND
時的位置關系StrokeCap
設置 為Paint.Cap.ROUND
,直線兩側的半圓是不算在直線的長度里的。簡單來說,如果view的長度和我畫的直線長度一致,那么這個直線就是矩形而不是期待的兩側都是半圓的橢圓。 -
當進度為0或者為100%時的展示
如果我們背景直線和指示直線起點終點位置一致,那么最終效果就是當進度為0或者100%的時候,指示直線是顯示不全的,因此我們需要對這兩個位置進行矯正。我的矯正方法比較簡單,在0~x和y~100%進度的時候分別顯示x和y(本以為自己的思路很屌,后來發現別人也是這么干的)。
-
-
CircleIndicator (中間的多種顏色環)
這個View我將其分解為四部分,從上層到下層:中間指針,刻度環,顯示的內容,圓環背景和外層指示文字。每一部分再做分解:
-
中間指針
這個又分解為六個部分:最下層大圓,兩個圓形半圓,兩個三角形和上層小圓。
-
刻度環
由于是圓環,所以必須畫弧線了。但是這個弧線又有點特殊,兩側帶圓角,中間圓環直角,這個沒想到啥好辦法,用了很一般的思路:先用
Paint.Cap.ROUND
畫兩側的弧,然后Paint.Style.STROKE
畫中間的部分。 -
顯示的內容
這個就比較簡單了,主要在于計算文字的顯示位置。
-
圓環背景和外層文字
最簡單的一個圓,加一個以圓為路徑畫的文字。
-
最后我們畫的順序正好與上面所述順序相反。
-
ProgressIndicator (最下面的兩種顏色環)
這個相較 CircleIndicator 就簡單了一些。在畫圓環的時候,我們只需要先畫一個灰色背景,然后再畫一個綠色圓弧即可。
0x02 實現
View的繪制我們應該都比較熟悉,主要有下面三個過程: onMeasure(測量)、onLayout(布局)、onDraw(繪制)。針對上述三個View:LineIndicator,我們根據實際的內容來計算View的高度,寬度用戶設定;CircleIndicator和ProgressIndicator的寬度用戶設定,高度自定計算與寬度相同。onLayout我們不需要。最后的根據實際的展示內容來繪制。
另外,由于CircleIndicator和ProgressIndicator和相似度高,很多代碼可以拿來重用,因此,我寫了一個基類來完成公共的部分,特殊部分每個 子類自己完成。最后再子類地onDraw方法中按照順序調用即可。
由于主要是計算各種坐標位置,代碼還是不少的,這里就不貼代碼。最后的實現請移步我的 github。三個View的代碼已經整理完LineIndicator,其它的兩個還需要點時間(2016-06-19)。
0x03 知識點
-
自定義View的思路
- 創建類,繼承自View或者ViewGroup或者其它ViewGroup
- 定義View的可配置參數,如果你需要的話
- 實現View的具體邏輯
這個太粗略了,各位可以參考具體源碼。
-
文字位置的計算
如果你沒有自己畫過文字,那你肯定不可能一下子明白文字的畫法,或多或少的出現偏移。這里直接給大家推薦一篇講的非常詳細的文章:http://blog.csdn.net/aigestudio/article/details/41447349
-
位置、半徑、弧長等的計算
這里會涉及到簡單的三角函數和圓的周長弧度的計算,可能要說的就是三角函數的參數是弧度制度。
-
ObjectAnimator
/** * 設置內容的顏色值(非resource的id) * * @param contentColor 內容的顏色值 * @param unitColor 單位的顏色值 */ public void setContentColor(int contentColor, int unitColor) { mContentColor = contentColor; mUnitColor = unitColor; } /** * 設置進度 * * @param indicator 進度值 */ public void setIndicator(float indicator) { if (indicator <= mStartIndicator) { mIndicator = mStartIndicator; } else if (indicator > mEndIndicator) { mIndicator = mEndIndicator; } else { mIndicator = indicator; } postInvalidate(); } /** * 獲取進度 * * @return 當前進度值 */ public float getIndicator() { return mStartIndicator; } public void animateIndicator(float indicator) { Interpolator interpolator = new AnticipateOvershootInterpolator(1.8f); ObjectAnimator animation = ObjectAnimator.ofFloat(this, "indicator", indicator); animation.setDuration(2000); animation.setInterpolator(interpolator); animation.start(); }
-
處理屏幕的旋轉
由于時間比較緊,這個我還沒做。主要在View的以下兩個方法中實現:
@Override protected Parcelable onSaveInstanceState() { return super.onSaveInstanceState(); } @Override protected void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(state); }
最后歡迎各位吐槽并提供更好的建議~~
