Kotlin中实现星级评价选择功能(仅支持整数)

效果图

1.在res下values添加attr.xml

XML 复制代码
        <declare-styleable name="CustomRatingBar">
            <attr name="starCount" format="integer" /><!-- 总共有几颗星-->
            <attr name="rating" format="float" /><!-- 默认选择几颗星-->
            <attr name="starSpacing" format="dimension" /><!--padding值-->
            <attr name="starEmpty" format="reference" /><!--选中后的图片-->
            <attr name="starFull" format="reference" /><!--未选中时的图片-->
        </declare-styleable>

2.自定义View(支持手势左右滑动选中)

Kotlin 复制代码
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.MotionEvent
import android.widget.ImageView
import android.widget.LinearLayout

class CustomRatingBar @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {

    private var starCount = 5
    var currentRating = 0f
    var mRating = 0f
    private var starSpacing = 8
    private val starViews = mutableListOf<ImageView>()

    init {
        orientation = HORIZONTAL
        gravity = Gravity.CENTER_VERTICAL
        // 解析 XML 中的自定义属性
        context.obtainStyledAttributes(attrs, R.styleable.CustomRatingBar).apply {
            starCount = getInt(R.styleable.CustomRatingBar_starCount, 5)
            currentRating = getFloat(R.styleable.CustomRatingBar_rating, 0f)
            mRating = getFloat(R.styleable.CustomRatingBar_rating, 0f)
            starSpacing = getDimensionPixelSize(R.styleable.CustomRatingBar_starSpacing, 8)
            recycle()
        }
        initStars()
    }

    private fun initStars() {
        removeAllViews()
        for (i in 0 until starCount) {
            val star = ImageView(context).apply {
                layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
                    if (i > 0) leftMargin = starSpacing
                }
                setOnClickListener { setRating((i + 1).toFloat()) }
            }
            addView(star)
            starViews.add(star)
        }
        updateStars()
    }

    fun setRating(rating: Float) {
        currentRating = rating.coerceIn(mRating, starCount.toFloat())
        updateStars()
    }

    private fun updateStars() {
        starViews.forEachIndexed { index, imageView ->
            //向左滑动最少显示指定的默认选中数量
            val resId = if (index < currentRating || (index==0&&currentRating==mRating)) R.mipmap.star_true else R.mipmap.star_false
            imageView.setImageResource(resId)
        }
    }

    // 支持手指滑动评分
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
                val newRating = (event.x / width * starCount).toInt() + 1
                setRating(newRating.toFloat())
                return true
            }
        }
        return super.onTouchEvent(event)
    }
}

3.xml中使用

XML 复制代码
	<LinearLayout
        android:layout_marginTop="10dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/black"
            android:textSize="15dp"
            android:text="星级评分:"/>
        <CustomRatingBar
            android:id="@+id/customRatingBar"
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            app:starCount="5"
            app:rating="1"
            app:starSpacing="12dp"
            app:starEmpty="@mipmap/star_true"
            app:starFull="@mipmap/star_false" />
    </LinearLayout>

4.代码中获取当前选中几颗星

XML 复制代码
val choose = binding.customRatingBar.currentRating.toInt()

图片元素(效果图中使用的图片,可替换自己的)

相关推荐
用户938515635078 小时前
从零在浏览器里跑 DeepSeek-R1:WebGPU + Transformer.js 全链路实战
前端·设计模式·typescript
CodeSheep8 小时前
稚晖君公司人事大变动,来了!
前端·后端·程序员
鱼樱前端8 小时前
用 AI 做内容变收入
前端·人工智能·ai编程
浮生望8 小时前
React + TypeScript 组件设计进阶:从类型约束到无状态组件的三次进化
前端
90后的晨仔9 小时前
Mac 开发 uni-app 终极方案:让安卓模拟器彻底脱离 Android Studio 独立运行
前端·vue.js
90后的晨仔9 小时前
别再搞混了!uni-app 中 node_modules 和 uni_modules 到底是什么关系?
前端
kyriewen10 小时前
面试官问"这段逻辑为什么这样写"——我才发现,这半年我写的代码,我自己都解释不了
前端·javascript·面试
YB137510 小时前
jetpack compose 副作用 SideEffect
android·kotlin
谢小飞10 小时前
大文件上传很难?学会这招,GB文件秒传不是梦
前端·node.js
程序员黑豆11 小时前
Windows 系统 Java 环境变量配置全攻略:解决“不是内部或外部命令”
java·前端·ai编程