Android自定義的評分控件,類似RatingBar那樣的,使用星星圖示(full、half、empty)作為rating值的“評分/打分控件”。
圖1:
RatingStarView控件支援的特性:

半顆星支援(實際支援任意小數)
填充色、底色、描邊色
指定高度,寬度自适應
拐角弧度、描邊寬度、星星間距
肥胖指數(star thickness),越胖越可愛
點選評分(不支援半顆星)
下面是RatingStarView的實作設計。
可以在抽象的xOy坐标系中計算得到一個star的“标準坐标”。這個坐标可以作為後續有關坐标計算(偏移和縮放)的基準。
圖2:
以上面的圖為例,這裡其中心點O為原點。
這裡為了描述友善,稱A,B,C,D,E為5個外點(Outer Corner Vertex),a,b,c,d,e 五個點為内點(Inner Corner Vertex)。
這裡坐标值的選取完全出于計算友善來考慮,實作方式畢竟很多,大家可以選取其它坐标方式,比如原點O的位置在其它處,或者星星的範圍由高度、寬度表示等。
A拐點的坐标為(0,1),其它幾個點的坐标根據幾何公式是可以固定下來的。為了簡化計算,可以将這幾個值作為常量儲存,之後的其它值的計算基于它們。下面代碼為了程式上的便利E點坐标x,y值是起始元素:
使用常量簡化五角星坐标計算時的cos、sin操作。因為幾何上這些點的坐标是固定的。之後可以通過簡單的+-*/操作來變換坐标系,以及star的大小。 常量也不會保持得太多,比如a,b,c,d,e的計算是根據A,B,C,D,E來的。
這裡為star引入“胖度系數(star thickness)”的說法,用來控制星星的可愛程度。
很明顯,胖度是由a,b,c,d,e五個内點的位置決定的。
但在計算上,這裡采取另一種方式:
設定變量<code>thickness</code>來表示肥胖系數,5個内點的位置由原點O和此内點臨近的兩個外點計算得到。
還是上面的圖2,
AE的中點是P,那麼e肯定在OP上,如果取OP上的其它點,作為EPA這樣的多邊形路徑(其它五個内點類似)就可以打造出不同肥胖度的星星了。
這裡因為原點O是星星的中心,在标準坐标系下,根據胖度系數thickness,結合ABCDE這幾個外點,就可以計算出abcde這幾個内點了,而且當thickness不同時,星星胖度不同。
根據thickness和ABCDE計算abcde的過程必須是在“标準坐标系”下,也就是X+軸向右,Y+向上,而且O原點是星星中心!!
每一個要顯示的star由一個<code>StarModel</code>類來表示,它持有一個星星的坐标資訊并完成相應的計算。
其代碼是整個RatingStarView關于坐标部分的核心,完整代碼見下面的源碼位址。
星星的頂點可以用一個PointF進行表示,不過這裡為了友善将多個點作為一個連結清單使用,定義了下面的<code>VertexF</code>來儲存頂點資料:
StarModel類使用靜态的數組儲存ABCDE五個外點的标準坐标系下的坐标初始值。
因為thickness系數必須在标準坐标系下計算,這裡選擇StarModel的構造函數中接受thickness參數,而且初始化中完成所有10個拐點的計算。
手機裝置下,Android的Y+是向下的,是以需要一個adjustCoordinate()的方法來完成星星坐标系的轉換。
同時它還将星星的x,y都變為正數——這樣它才是可見的。
注意Android中,childView繪制自身内容時,其使用的x,y坐标機關是pixel,而且是相對其父ViewGroup的相對坐标。
RatingStarView在顯示若幹個star時,需要可以控制其位置和大小。
是以StarModel在标準坐标系轉換完為Android下坐标系後(在父布局中的相對坐标),還需要可以被偏移和縮放。
隻需要對10個拐點坐标進行+、-操作即可。
有關Star的大小這裡使用height來衡量,因為繪制肯定是完整的星星,這樣height和width是有一個比例的。選取height或width作為其大小衡量本身都可以。
首先以star的height作為衡量,那麼在标準坐标系進行轉換後可以認為star是具備一個預設的縮放系數的:就是它的高度AD(或AC)線段的垂直距離。
之後要為star設定新的高度時(也就是改變其大小範圍——外接矩形邊框outerRect),根據高度的變化進行乘除運算即可——要注意的是坐标問題,這個留給寫代碼時思考。😃
以上是關于坐标和坐标相關的計算,主要由<code>StarModel</code>類完成,它持有要顯示的每一個star的資料。
繪制的功能由RatingStarView實作,它繼承了View類:
自定義控件第一步解決自身大小的測量問題。
前面提到了star的大小由其height決定。
為了同時顯示多個star,而且考慮文章開頭宣稱的那些特性,RatingStarView如何測量自身大小的邏輯也就确定了。
要注意,View測量時的一般準則是需要遵循的:MATCH_PARENT這樣的不限定大小的情況——此時還是優先确定height。
在onMeasure()中:
計算時原則是先确定View的height,作為star的高度。
考慮padding,starMargin(星星間距)。
因為是float值相關計算,測量最終大小應該取“向上”的整數。
RatingStarView不是ViewGroup,它不需要布局childView。
但需要根據自身大小确定要顯示的各個star的坐标資料。
在onSizeChanged()中監聽View大小變化,并計算要顯示的star(多個)的坐标資料,也就是<code>private ArrayList<StarModel> starList</code>:
<code>Canvas.drawPath()</code>可以用來繪制若幹個點組成的閉合path。
方法原型:
但為了繪制“圓角五角星”,需要設定paint的“路徑效果”:
這裡設定<code>public class CornerPathEffect extends PathEffect</code>即可。
paint可設定其Style。
fullStar:<code>Paint.Style.FILL_AND_STROKE</code>
emptyStar:<code>Paint.Style.STROKE</code>
Canvas支援圖層操作。
可以在第一層繪制空星。
然後在新的圖層中繪制滿星——并利用<code>canvas.clipRect(clip);</code>來裁剪出一半星星。因為
clipRect是一個矩形,是以其實可以繪制任意小數的星星——隻不過0.5(半星)最好看。
良好的控件需要支援java代碼和xml中建立及設定它的各個方面。
RatingStarView支援:
設定Star數量
設定Rating (float值,一個star做1看待)
設定thickness
其它的間距、大小、寬度、顔色等
它是開源的,你可以自行修改和擴充
RatingStarView支援點選評分,不支援半星——半星這種是許多使用者評分後的均值。
在onTouchEvent()中記錄點選的x,y坐标:
RatingStarView自己實作View.OnClickListener,監聽自身點選。
在onClick()回調中根據顯示的starList,以及自身大小來改變Rating.
預設它隻用來展示評分(隻讀),可以通過enableSelectRating屬性開啟點選評分。
見這裡
"https://jitpack.io/#everhad/AndroidRatingStar/v1.0.1".
完整代碼見這裡:
https://github.com/everhad/AndroidRatingStar
希望它能節約你的時間(去和UI要各種icon定制RatingBar)
我的部落格即将搬運同步至騰訊雲+社群,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan
(本文使用Atom編寫)