美文网首页
自定义 View 实战 06 - 仿写 RatingBar

自定义 View 实战 06 - 仿写 RatingBar

作者: Kotyo | 来源:发表于2020-06-30 17:17 被阅读0次
    RatingBar

    效果分析:

    star_normal.png star_select.png

    准备两张星星图,一张默认,一张选中。初始的时候绘制默认的 5 颗星星,然后根据手势绘制选中的星星。

    自定义属性

    <declare-styleable name="KRatingbar">
            //要绘制的个数
            <attr name="starNum" format="integer"/>
            //星星之间的间距
            <attr name="starPadding" format="integer" />
            //默认星星的 resId
            <attr name="starNormal" format="reference" />
            //选中星星的 resId
            <attr name="starSelect" format="reference" />
    </declare-styleable>
    

    具体实现

    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)
            //图片宽度*绘制的个数+星星间的间隙+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 -> {
                    //根据手指按下的 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
                    }
                    //越界时默认为最大的绘制个数
                    if (count > mCount) {
                        mCurSelect = mCount
                    }
                    //当前选中了第几个
                    mCurSelect = count
                    //重绘
                    invalidate()
                }
            }
            //这里要返回 true 才会处理 onTouchEvent 事件
            return true
        }
    
    }
    

    项目地址

    相关文章

      网友评论

          本文标题:自定义 View 实战 06 - 仿写 RatingBar

          本文链接:https://www.haomeiwen.com/subject/wdpxqktx.html