效果分析:
準備兩張星星圖憔杨,一張默認,一張選中。初始的時候繪制默認的 5 顆星星尿赚,然后根據(jù)手勢繪制選中的星星。
自定義屬性
<declare-styleable name="KRatingbar">
//要繪制的個數(shù)
<attr name="starNum" format="integer"/>
//星星之間的間距
<attr name="starPadding" format="integer" />
//默認星星的 resId
<attr name="starNormal" format="reference" />
//選中星星的 resId
<attr name="starSelect" format="reference" />
</declare-styleable>
具體實現(xiàn)
class KRatingbar @JvmOverloads constructor(
context: Context,
attributes: AttributeSet?,
defStyle: Int = 0
) : View(context, attributes, defStyle) {
private var mCount = 5
private var mNormalBitmap: Bitmap
private var mSelectBitmap: Bitmap
private var mStarPadding = dp2Px(5, resources)
private var mCurSelect = -1
init {
val ta = context.obtainStyledAttributes(attributes, R.styleable.KRatingbar)
mCount = ta.getInt(R.styleable.KRatingbar_starNum, 5)
mStarPadding = ta.getInt(R.styleable.KRatingbar_starPadding, dp2Px(5, resources))
val normalId = ta.getResourceId(R.styleable.KRatingbar_starNormal, -1)
if (normalId == 1.unaryMinus()) throw RuntimeException("starNormal can't be empty")
mNormalBitmap = BitmapFactory.decodeResource(resources, normalId)
val selectId = ta.getResourceId(R.styleable.KRatingbar_starSelect, -1)
if (selectId == 1.unaryMinus()) throw RuntimeException("starSelect can't be empty")
mSelectBitmap = BitmapFactory.decodeResource(resources, selectId)
ta.recycle()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//圖片寬度*繪制的個數(shù)+星星間的間隙+paddingLeft+paddingRight
val width =
mNormalBitmap.width * mCount + mStarPadding * (mCount - 1) + paddingLeft + paddingRight
//圖片的高度+paddingTop+paddingBottom
val height = mNormalBitmap.height + paddingTop + paddingBottom
setMeasuredDimension(width, height)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
for (i in 0 until mCount) {
var x = i * mNormalBitmap.width
if (i != 0) {
//平移畫布蕉堰,其實就是在繪制間隙
canvas.translate(mStarPadding.toFloat(), 0f)
}
if (i < mCurSelect) {
//繪制選中的星星
canvas.drawBitmap(mSelectBitmap, x.toFloat(), 0f, null)
} else {
//繪制默認星星
canvas.drawBitmap(mNormalBitmap, x.toFloat(), 0f, null)
}
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP -> {
//根據(jù)手指按下的 x 坐標去計算按壓的是第幾個星星
//這里要 +1凌净,因為 index 是從 0 開始的
val count = event.x.toInt() / (mNormalBitmap.width+mStarPadding) + 1
//當前選中的和之前一樣不再重復繪制,count !=1 是為了處理屋讶,按壓第一個時冰寻,無法滑動選中的 bug
if (mCurSelect == count && count != 1) {
return false
}
//越界時為 0
if (count < 0) {
mCurSelect = 0
}
//越界時默認為最大的繪制個數(shù)
if (count > mCount) {
mCurSelect = mCount
}
//當前選中了第幾個
mCurSelect = count
//重繪
invalidate()
}
}
//這里要返回 true 才會處理 onTouchEvent 事件
return true
}
}