Android用來替代RatingBar的自定義ImageRatingView
因為android自帶的RatingBar用起來不是很方便,于是決定自己寫一個替代品。
控件介紹:
示意圖
<declare-styleable name="ImageRatingView"> //設置評分最大值 <attr name="maxCount" format="integer" /> //設置評分之間的跨度,示意圖中為0.5 <attr name="minStep" format="float" /> //設置評分時的圖片 <attr name="frontImage" format="reference"/> //設置未評分的圖片(需和評分的圖片等寬等高) <attr name="backImage" format="reference"/> //圖片之間的間隔 <attr name="spanSize" format="dimension"/> //默認評分 <attr name="rating" format="float"/> //是否可觸摸評分 <attr name="touchable" format="boolean"/> //設定圖片的寬(layout_width設置為wrap_content即可) <attr name="imageWidth" format="dimension"/> //設定圖片的高(layout_height設置為wrap_content即可) <attr name="imageHeight" format="dimension"/> </declare-styleable>
在代碼中支持設定默認評分以及獲取評分,還可以通過設置 OnRatingChangedListener 來監聽評分的變化。
基本思路:
利用 canvas.drawBitmap(bitmap,src,dst,paint) 來繪制評分的圖標。照圖上來講分三部分進行繪制:①繪制前三個黃色的星星、②繪制后面三個白色的星星、③第四個星星單獨繪制。
代碼實現:
- 繪制前三個黃色的星星
private void drawFront(Canvas canvas) { //src是指圖片要顯示的部分,這里為顯示整個星星。 Rect src = new Rect(0, 0, mFront.getWidth(), mFront.getHeight()); for (int i = 0; i < (int) mRating; i++) { int left = i * (mBitmapDstWidth + mSpanSize); int right = (i + 1) * mBitmapDstWidth + i * mSpanSize; //dst指圖片在view中顯示的位置,這里動態計算出每個星星的坐標位置。 Rect dst = new Rect(left, 0, right, mBitmapDstHeight); canvas.drawBitmap(mFront, src, dst, null); } }
- 繪制后三個白色的星星
private void drawBack(Canvas canvas) { Rect src = new Rect(0, 0, mFront.getWidth(), mFront.getHeight()); //和繪制前三個的主要區別是這里的起始值不同 for (int i = (int) (mRating + 1); i < mMaxCount; i++) { //mBitmapDstWidth是圖片指定的寬度 int left = i * (mBitmapDstWidth + mSpanSize); int right = (i + 1) * mBitmapDstWidth + i * mSpanSize; Rect dst = new Rect(left, 0, right, mBitmapDstHeight); canvas.drawBitmap(mBack, src, dst, null); } }
-
繪制第四個星星
private void drawLastFront(Canvas canvas) { //先畫front部分即黃色的部分 //rating小數點前的值。 int rating = (int) mRating; //算出rating小數點后的值,圖中實例為0.5 float frontPart = mRating - rating; int frontWidth = (int) (frontPart * mFront.getWidth()); //這里src的寬度就是圖片的一半 Rect srcFront = new Rect(0, 0, frontWidth, mFront.getHeight()); //位置的起始值和之前的一樣 int leftFront = rating * (mBitmapDstWidth + mSpanSize); //區別在結束值,按照圖中實例來理解就是3.5*圖片的寬度+3*圖片之間的間隔 int rightFront = (int) (mRating* mBitmapDstWidth + rating * mSpanSize); Rect dstFront = new Rect(leftFront, 0, rightFront, mBitmapDstHeight); canvas.drawBitmap(mFront, srcFront, dstFront, null); //back部分即白色的部分 int backWidth = (int) (frontPart * mFront.getWidth()); Rect srcBack = new Rect(backWidth, 0, mFront.getWidth(), mFront.getHeight()); //位置的起始值即剛才畫前半部分的結束值 int leftBack = (int) (mRating * mBitmapDstWidth + rating * mSpanSize); int rightBack = (rating + 1) * mBitmapDstWidth + rating * mSpanSize; Rect dstBack = new Rect(leftBack, 0, rightBack, mBitmapDstHeight); canvas.drawBitmap(mBack, srcBack, dstBack, null); }
其他細節:
因為本控件支持設定最小評分的跨度,例如設置step為0.5,如果這時傳進來3.3分,就要四舍五入到3.5。代碼如下:
private void correctedRatingValue() { if (mRating > mMaxCount) { mRating = mMaxCount; } else if (mRating < 0) { mRating = 0; } //最后一個跨度 進行四舍五入的判斷 float after = mRating % mMinStep > mMinStep / 2 ? mMinStep : 0; //除去最后個跨度 float front = mRating - mRating % mMinStep; mRating = after +front; }
但在觸摸控件評分時上面的算法就不能用了,舉個例子例如我觸摸得到的評分是3.1,那么按照上面的算法會得到3分,最后就顯示為3個星星。這樣顯示就很怪異,因為正常情況你的手指觸摸在了星星上就表示這個星星會亮,因此這時候要換一種算法:
private void correctedRatingValueForTouch() { if (mRating % mMinStep == 0) return; mRating = mRating - mRating % mMinStep + mMinStep; }