IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android自定义View第五弹(可滑动的星星评价) -> 正文阅读

[移动开发]Android自定义View第五弹(可滑动的星星评价)

个人开发中自定义View系列(有需要的可以点击查看收藏)

  1. Android自定义view第一弹(防小米计步)
  2. Android自定义View第二弹(旋转的体重)
  3. Android自定义View第三弹(反人类尺子)
  4. Android自定义View第四弹(Kotlin流式布局)

距离上一篇自定义view已经过去了一年多了,这次主要给大家介绍的是可滑动的星星评价,虽然Google官方也提供了 RatingBar 但是没办法满足我的需要只能自己定义一个了,废话不多说先上图:
在这里插入图片描述
这个选中以及默认的心型都是UI提供的图片,上代码:

1.自定义view的代码

import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import cn.neoclub.uki.R
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.roundToInt

/**
 * Author: Mr.Dong
 * Date: 2022/2/15 4:31 下午
 * Description: 点击心心评价
 */
class HeartRatingBar : View {
    private var starDistance = 0 //星星间距
    private var starCount = 5 //星星个数
    private var starSize = 0 //星星高度大小,星星一般正方形,宽度等于高度
    private var starMark = 0 //评分星星
    private var starFillBitmap: Bitmap? = null //亮星星
    private var starEmptyDrawable : Drawable? = null//暗星星
    private var onStarChangeListener : OnStarChangeListener? = null//监听星星变化接口
            
    private var paint : Paint? = null//绘制星星画笔
    //是否显示整数的星星       
    private var integerMark = false
    //初始化可以被定义为滑动的距离(超过这个距离就是滑动,否则就是点击事件)
    private var scaledTouchSlop:Int=0

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        init(context, attrs)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
        init(context, attrs)
    }

    /**
     * 初始化UI组件
     *
     * @param context
     * @param attrs
     */
    private fun init(context: Context, attrs: AttributeSet?) {
        //获取滑动的有效距离
        scaledTouchSlop=ViewConfiguration.get(context).scaledTouchSlop
        isClickable = true
        //获取各种属性的值
        val mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.HeartRatingBar)
        starDistance = mTypedArray.getDimension(R.styleable.HeartRatingBar_starDistance, 0f).toInt()
        starSize = mTypedArray.getDimension(R.styleable.HeartRatingBar_starSize, 20f).toInt()
        starCount = mTypedArray.getInteger(R.styleable.HeartRatingBar_starCount, 5)
        starEmptyDrawable = mTypedArray.getDrawable(R.styleable.HeartRatingBar_starEmpty)
        starFillBitmap = drawableToBitmap(mTypedArray.getDrawable(R.styleable.HeartRatingBar_starFill))
        mTypedArray.recycle()
        paint = Paint()
        //设置抗锯齿
        paint?.isAntiAlias = true
        //设置渲染器
        paint?.shader = BitmapShader(starFillBitmap!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
    }

    /**
     * 设置是否需要整数评分
     * @param integerMark
     */
    fun setIntegerMark(integerMark: Boolean) {
        this.integerMark = integerMark
    }

    /**
     * 设置显示的星星的分数
     *
     * @param mark
     */
    private fun setStarMark(mark: Int) {
        starMark = if (integerMark) {
            //ceil函数 去除小数点后面的 返回 double 类型,返回值大于或等于给定的参数 例Math.ceil(100.675) = 101.0
            ceil(mark.toDouble()).toInt()
        } else {
            (mark * 10).toFloat().roundToInt() * 1 / 10
        }
        if (onStarChangeListener != null) {
            onStarChangeListener?.onStarChange(starMark) //调用监听接口
        }
        invalidate()
    }

    /**
     * 获取显示星星的数目
     *
     * @return starMark
     */
    fun getStarMark(): Int {
        return starMark
    }

    /**
     * 定义星星点击的监听接口
     */
    interface OnStarChangeListener {
        fun onStarChange(mark: Int)
    }

    /**
     * 设置监听
     * @param onStarChangeListener
     */
    fun setOnStarChangeListener(onStarChangeListener: OnStarChangeListener?) {
        this.onStarChangeListener = onStarChangeListener
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        //设置view的宽度和高度 继承view必须重写此方法
        setMeasuredDimension(starSize * starCount + starDistance * (starCount - 1), starSize)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (starFillBitmap == null || starEmptyDrawable == null) {
            return
        }
        //绘制空的星星
        for (i in 0 until starCount) {
            //设置starEmptyDrawable绘制的长方形区域,当调用draw()方法后就可以直接绘制
            starEmptyDrawable?.setBounds(
                (starDistance + starSize) * i,
                0,
                (starDistance + starSize) * i + starSize,
                starSize
            )
            starEmptyDrawable?.draw(canvas)
        }
        if (starMark > 1) {
            //绘制了第一个star
            canvas.drawRect(0f, 0f, starSize.toFloat(), starSize.toFloat(), paint!!)
            if (starMark - starMark == 0) { //第一步必走这里 
                //绘制亮星星
                for (i in 1 until starMark) {
                    //每次位移start的宽度+间距
                    canvas.translate((starDistance + starSize).toFloat(), 0f)
                    canvas.drawRect(0f, 0f, starSize.toFloat(), starSize.toFloat(), paint!!)
                }
            } else { //非整形的star绘制走这里
                for (i in 1 until starMark - 1) {
                    canvas.translate((starDistance + starSize).toFloat(), 0f)
                    canvas.drawRect(0f, 0f, starSize.toFloat(), starSize.toFloat(), paint!!)
                }
                canvas.translate((starDistance + starSize).toFloat(), 0f)
                canvas.drawRect(
                    0f,
                    0f,
                    starSize * (((starMark - starMark) * 10).toFloat().roundToInt() * 1.0f / 10),
                    starSize.toFloat(),
                    paint!!
                )
            }
        } else {
            //startMark=0 啥都没绘制
            canvas.drawRect(0f, 0f, (starSize * starMark).toFloat(), starSize.toFloat(), paint!!)
        }
    }

    //记录一下上次down的x的位置
    private var downX:Int=0

    override fun onTouchEvent(event: MotionEvent): Boolean {
        var x = event.x.toInt()
        if (x < 0) x = 0
        if (x > measuredWidth) x = measuredWidth
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                downX=x
                //对于除数不能为0的限制
                if(starCount==0||(measuredWidth * 1 / starCount)==0){
                    return false
                }
                val count=x * 1 / (measuredWidth * 1 / starCount)
                setStarMark(count+1)
            }
            MotionEvent.ACTION_MOVE -> {
                //当滑动距离的绝对值小于官方定义的有效滑动距离则不走move当做down处理
                if(abs(event.x-downX)<scaledTouchSlop){
                    return false
                }
                if(starCount==0||(measuredWidth * 1 / starCount)==0){
                    return false
                }
                setStarMark(x * 1 / (measuredWidth * 1 / starCount))
            }
            MotionEvent.ACTION_UP -> {}
        }
        invalidate()
        return super.onTouchEvent(event)
    }

    /**
     * drawable转bitmap
     *
     * @param drawable
     * @return
     */
    private fun drawableToBitmap(drawable: Drawable?): Bitmap? {
        if (drawable == null) return null
        val bitmap = Bitmap.createBitmap(starSize, starSize, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        drawable.setBounds(0, 0, starSize, starSize)
        drawable.draw(canvas)
        return bitmap
    }
}

2.自定义View的使用

    <cn.neoclub.uki.message.widget.HeartRatingBar
        android:id="@+id/rb_rating_bar"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        app:starCount="5"
        app:starDistance="7dp"
        app:starEmpty="@drawable/icon_heart_rating_default"
        app:starFill="@drawable/icon_heart_rating_select"
        app:starSize="40dp" />

3.attrs.xml文件中的属性

 <declare-styleable name="HeartRatingBar">
        <attr name="starDistance" format="dimension"/>
        <attr name="starSize" format="dimension"/>
        <attr name="starCount" format="integer"/>
        <attr name="starEmpty" format="reference"/>
        <attr name="starFill" format="reference"/>
    </declare-styleable>

4.送你两张图,怕你运行不起来

1.icon_heart_rating_select.png
在这里插入图片描述
2.icon_heart_rating_default.png
在这里插入图片描述
是不是感觉这边少了个icon,对了就是少一张😄(其实是有图的)

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-02-22 20:43:01  更:2022-02-22 20:43:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 15:19:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码