Kotlin-->自定义评分控件RatingBar_第1张图片

首先了解下, 自定义View的三部曲.

1:onMeasure方法

此方法主要目的, 就是根据xml的
android:layout_width="wrap_content"
android:layout_height="wrap_content"

wrap_content match_parent 这2个属性, 来确定测量自身的大小.
当然, 这2个值, 只是parent告诉你, 需要按照此规则来测量, 如果你是一个坏孩子, 那么可以无视测量规则, 任意设置一个宽度和高度, 比如: setMeasuredDimension(1万, 2万) 就是如此简单;

2:onLayout方法

如果你是自定义View, 此方法可以不必override
如果你是自定义的ViewGroup, 那么就必须override, 此方法的目的就是由你决定child view在界面上的位置.

3:onDraw方法

在这个方法里面, 你可以展开你天才般的做图功能, 想画啥就画啥. 美美的view, 就这样出来了.
友情提示:如果你是自定义的ViewGroup, 还需要调用setWillNotDraw(false)方法, 否则onDraw方法不会执行哦


开始本文:

拿到需求, 先分析一波, 不着急上手. 因为你的分析步骤, 严重影响了后续的开发效率.
分析的越细, 实现越轻松.

1: 星星有2种状态, 普通和高亮, 可以用2个drawable成员变量搞定
2: 星星的个数是个变化的值, 可以用一个成员变量搞定
3: 星星之间的间隙, 可以用一个成员变量搞定

很简单的需求, 所以步骤不多. 总之, 问题就是靠每一个步骤解答出来的.

1:自定义一个RRatingBar 继承自 View

class RRatingBar(context: Context, attributeSet: AttributeSet? = null) : View(context, attributeSet) {}

声明2个成员变量, 保存2种星星的状态

    /*五角星 选中的 图案*/    var ratingSelectorDrawable by LayoutProperty(getDrawable(R.drawable.base_wujiaoxing_20))    /*五角星 未选中的 图案*/    var ratingNormalDrawable by LayoutProperty(getDrawable(R.drawable.base_wujiaoxing_20_n))

kotlin 属性代理的使用

//这个属性代理的作用, 就是在属性被赋值的时候, 自动调用`requestLayout()`方法class LayoutProperty<T>(var value: T) : ReadWriteProperty<View, T> {    override fun getValue(thisRef: View, property: KProperty<*>): T = value    override fun setValue(thisRef: View, property: KProperty<*>, value: T) {        this.value = value        thisRef.requestLayout()    }}

声明星星的数量

   /**星星的数量*/    var ratingNum: Int by RefreshProperty(5)

同样用了一个属性代理postInvalidate():

/*定义一个自动刷新的属性代理*/class RefreshProperty<T>(var value: T) : ReadWriteProperty<View, T> {    override fun getValue(thisRef: View, property: KProperty<*>): T = value    override fun setValue(thisRef: View, property: KProperty<*>, value: T) {        this.value = value        thisRef.postInvalidate()    }}

声明星星的间隙

    /**星星之间的间隙*/    var ratingSpace: Float by RefreshProperty(8 * density)

2:onDraw

到这里, 其实就已经可以绘制出星星了

  override fun onDraw(canvas: Canvas) {       //调用此方法, 不禁用view本身的功能, 比如:原本的background属性       super.onDraw(canvas)       //确保星星的有效数量       val num = Math.max(0, Math.min(curRating, ratingNum))       //绘制未选中星星       var left = paddingLeft       for (i in num until ratingNum) {           canvas.save()           canvas.translate(left + i * ratingWidth + i * ratingSpace, paddingTop.toFloat())           ratingNormalDrawable.draw(canvas)           canvas.restore()       }       //绘制选中星星       left = paddingLeft       for (i in 0 until num) {           canvas.save()           canvas.translate(left + i * ratingWidth + i * ratingSpace, paddingTop.toFloat())           ratingSelectorDrawable.draw(canvas)           canvas.restore()       }   }

但是, 这个时候还没有处理手势touch事件. 所以星星不能响应手势操作.

3:onTouchEvent手势

根据手势的x坐标 和 每个星星的中心点x的左边, 如果手势x大于星星x, 那么肯定选中了.以此类推, 找到最后一个星星.

 override fun onTouchEvent(event: MotionEvent): Boolean {        val action = MotionEventCompat.getActionMasked(event)        when (action) {            MotionEvent.ACTION_DOWN -> {                parent.requestDisallowInterceptTouchEvent(true)                calcRating(event.x)            }            MotionEvent.ACTION_MOVE -> {                calcRating(event.x)            }            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {                parent.requestDisallowInterceptTouchEvent(false)            }        }        return true    }    /*根据x的坐标, 计算星星的个数*/    private fun calcRating(x: Float) {        var left = paddingLeft        var index = 0        for (i in 0 until ratingNum) {            if (x > left + i * ratingWidth + i * ratingSpace) {                index = i + 1            }        }        if (index.minValue(minRating) != curRating) {            curRating = index            postInvalidate()        }    }

联系作者

请使用QQ扫码加群, 小伙伴们在等着你哦!

关注我的公众号, 每天都能一起玩耍哦!

更多相关文章

  1. Android文件系统的结构及目录用途、操作方法
  2. 屏幕旋转会重启onCreate方法
  3. Android ADT 找不到Annotation Processing选项,解决方法。
  4. 进程方法Android进程与线程基本知识
  5. Android~两种将Activity设置成窗口样式的方法
  6. [Android Pro] Android的Animation之LayoutAnimation使用方法
  7. parseSdkContent failedCould not initialize class android.gra
  8. [转]android editText属性详细介绍
  9. Android系列之Intent传递对象的两种方法

随机推荐

  1. Android(安卓)RadioGroup 设置默认值之后
  2. Android(安卓)AndBox 安安卜1.3发布
  3. View类的XML属性、相关方法及说明
  4. Android(安卓)基础知识 学习总结
  5. Android在SDcard建文件夹(在Android中移动
  6. android:configChanges属性
  7. webkit for android
  8. Android(安卓)读取元素中的数据
  9. Android设置Activity背景为透明style
  10. Android调用系统默认浏览器访问的方法