Kotlin实现自定义RatingBar
2018-05-26 本文已影响61人
kksoCoud
好久没分享过了,突然感觉手都有点生疏了。最近需求ratingbar显示规则为:0为空星,1为全星,0.5为半星,小于0.5为小半星,大于0.5则为大半星。对于这个需求第一感觉就是使用系统自带RatingBar,但在使用过程中发现其只能实现空星、半星、全星,其所自带的android:stepSize=""
与android:rating=""
属性并不能很好的实现此种需求效果,并且网上实现的自定义Ratingbar,基本都是通过继承LinearLayout,内部创建ImageView来实现的,个人感觉这样实现不是特别好,于是决定自己动手丰衣足食。
基本思路是:通过继承View完全自定来实现(onMeasure、onDraw来手动测绘)。好了,bibi了这么多,还是先看效果图吧。
效果就是如上,基本需求已经满足了,嘿嘿。
下面我们来看看它的实现过程吧,基本就是撸代码啦。。。
//空星
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
//默认星星个数
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()
}
//星星总数
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)
}
}
}
/**
* 获取进度全星个数
*/
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>
好了,就这么简单