好久沒分享過了,突然感覺手都有點生疏了昂利。最近需求ratingbar顯示規(guī)則為:0為空星,1為全星铁坎,0.5為半星蜂奸,小于0.5為小半星,大于0.5則為大半星硬萍。對于這個需求第一感覺就是使用系統(tǒng)自帶RatingBar扩所,但在使用過程中發(fā)現(xiàn)其只能實現(xiàn)空星、半星朴乖、全星祖屏,其所自帶的android:stepSize=""
與android:rating=""
屬性并不能很好的實現(xiàn)此種需求效果,并且網(wǎng)上實現(xiàn)的自定義Ratingbar寒砖,基本都是通過繼承LinearLayout赐劣,內(nèi)部創(chuàng)建ImageView來實現(xiàn)的嫉拐,個人感覺這樣實現(xiàn)不是特別好哩都,于是決定自己動手豐衣足食。
基本思路是:通過繼承View完全自定來實現(xiàn)(onMeasure婉徘、onDraw來手動測繪)漠嵌。好了咐汞,bibi了這么多,還是先看效果圖吧儒鹿。
custom_rating_bar.png
效果就是如上化撕,基本需求已經(jīng)滿足了,嘿嘿约炎。
下面我們來看看它的實現(xiàn)過程吧植阴,基本就是擼代碼啦。圾浅。掠手。
//空星
private const val NONE = 0
//半星
private const val HALF = 1
//小半星
private const val HALF_LOW = 2
//大半星
private const val HALF_OVER = 3
//默認進度
private const val DEFAULT_RATING = 0.0f
//默認星星個數(shù)
private const val DEFAULT_NUM_STARS = 5
//默認星星間間距
private const val DEAFULT_SPACE_BETWEEN = 0.0f
class CustomeRatingBar : View {
//星星進度
var rating: Float = DEFAULT_RATING
set(value) {
field = value
invalidate()
}
//星星總數(shù)
var numStars: Int = DEFAULT_NUM_STARS
set(value) {
field = value
invalidate()
}
//星星間距
var spaceBetween: Float = DEAFULT_SPACE_BETWEEN
set(value) {
field = value
invalidate()
}
//空星drawable
private var mStrokeStar: BitmapDrawable? = null
//實星drawable
private var mFullStar: BitmapDrawable? = null
//半星drawable
private var mHalfStar: BitmapDrawable? = null
//小半星drawable
private var mHalfLowStar: BitmapDrawable? = null
//大半星drawable
private var mHalfOverStar: BitmapDrawable? = null
private val mPaint by lazy {
Paint(Paint.ANTI_ALIAS_FLAG)
}
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
initAttributeSet(attrs)
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initAttributeSet(attrs)
}
private fun initAttributeSet(attrs: AttributeSet?) {
context?.obtainStyledAttributes(attrs, R.styleable.CustomerRatingBar)?.let {
try {
rating = it.getFloat(R.styleable.CustomerRatingBar_rating, DEFAULT_RATING)
numStars = it.getInt(R.styleable.CustomerRatingBar_numStars, DEFAULT_NUM_STARS)
mStrokeStar = it.getDrawable(R.styleable.CustomerRatingBar_strokeStar) as BitmapDrawable?
mFullStar = it.getDrawable(R.styleable.CustomerRatingBar_fullStar) as BitmapDrawable?
mHalfStar = it.getDrawable(R.styleable.CustomerRatingBar_halfStar) as BitmapDrawable?
mHalfLowStar = it.getDrawable(R.styleable.CustomerRatingBar_halfLowStar) as BitmapDrawable?
mHalfOverStar = it.getDrawable(R.styleable.CustomerRatingBar_halfOverStar) as BitmapDrawable?
spaceBetween = it.getDimension(R.styleable.CustomerRatingBar_spaceBetween, DEAFULT_SPACE_BETWEEN)
} finally {
it.recycle()
}
if (mStrokeStar == null) {
throw IllegalArgumentException("must set stroke star drawable")
}
if (mHalfStar == null) {
throw IllegalArgumentException("must set half star drawable")
}
if (mFullStar == null) {
throw IllegalArgumentException("must set full star drawable")
}
}
}
fun setStrokeStar(resStrokeStar: Int) {
mStrokeStar = resources.getDrawable(resStrokeStar) as BitmapDrawable?
}
fun setFullStar(resFullStar: Int) {
mFullStar = resources.getDrawable(resFullStar) as BitmapDrawable?
}
fun setHalfStar(resHalfStar: Int) {
mHalfStar = resources.getDrawable(resHalfStar) as BitmapDrawable?
}
fun setHalfLowStar(resHalfLosStar: Int) {
mHalfLowStar = resources.getDrawable(resHalfLosStar) as BitmapDrawable?
}
fun setHalfOverStar(resHalfOverStar: Int) {
mHalfOverStar = resources.getDrawable(resHalfOverStar) as BitmapDrawable?
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var width = MeasureSpec.getSize(widthMeasureSpec)
var height = mStrokeStar?.bitmap?.height ?: 0
if (MeasureSpec.AT_MOST == MeasureSpec.getMode(widthMeasureSpec)) {
width = mStrokeStar?.bitmap?.let {
it.width * numStars + (spaceBetween * (numStars - 1) + 0.5f).toInt()
} ?: 0
}
if (MeasureSpec.EXACTLY == MeasureSpec.getMode(heightMeasureSpec)) {
height = MeasureSpec.getSize(heightMeasureSpec)
}
setMeasuredDimension(width, height)
}
override fun onDraw(canvas: Canvas?) {
var bitmap: Bitmap? = null
for (index in 0 until numStars) {
if (index < getFullStarCount()) {
//繪制全星
bitmap = mFullStar?.bitmap
} else {
if (getFullStarCount() == index) {
//繪制進度最后一個星星
when (getLastProgressType()) {
NONE -> bitmap = mStrokeStar?.bitmap
HALF -> bitmap = mHalfStar?.bitmap
HALF_LOW -> bitmap = mHalfLowStar?.bitmap
HALF_OVER -> bitmap = mHalfOverStar?.bitmap
}
} else {
//繪制剩余空星
bitmap = mStrokeStar?.bitmap
}
}
bitmap?.let {
canvas?.drawBitmap(it, index * (it.width + spaceBetween), 0.0f, mPaint)
}
}
}
/**
* 獲取進度全星個數(shù)
*/
private fun getFullStarCount() = rating.toInt()
/**
* 獲取進度最后一個星星類型
*/
private fun getLastProgressType() = (rating - getFullStarCount()).let {
when {
it == 0.0f -> NONE
it > 0.5f -> mHalfOverStar?.let { HALF_OVER } ?: HALF
it < 0.5f -> mHalfLowStar?.let { HALF_LOW } ?: HALF
else -> HALF
}
}
}
自定義屬性如下:
<declare-styleable name="CustomerRatingBar">
<attr name="rating" format="float"/>
<attr name="numStars" format="integer"/>
<attr name="strokeStar" format="reference"/>
<attr name="fullStar" format="reference"/>
<attr name="halfStar" format="reference"/>
<attr name="halfLowStar" format="reference"/>
<attr name="halfOverStar" format="reference"/>
<attr name="spaceBetween" format="dimension"/>
</declare-styleable>
好了,就這么簡單