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()

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

相关推荐
小码哥_常4 小时前
一套自己部署的 IM,客户端是同一份 Flutter
前端·后端
蜗牛攻城狮7 小时前
微前端四大方案选型对比与最佳实践:qiankun、micro-app、wujie、Module Federation
前端·微前端·qiankun·前端架构·micro-app·wujie
她说可以呀8 小时前
Vue插槽
前端·javascript·vue.js
qq_448011168 小时前
Vue + Jinja2两套模版与法冲突造成undefinedError
前端·javascript·vue.js
aixingpan8 小时前
aixingpan.cn API开发文档:前端查询夏令时和时区接口指南
前端·php
2601_958352909 小时前
23.5×19mm 换掉一整条音频链?|A-68 集成了什么:ADC / DAC / 24.576MHz 时钟 / AEC / 波束降噪,主控仅需 6 根线
前端·音视频·ai降噪·回声消除·语音模块
志尊宝10 小时前
AI原生前端开发(039):AI辅助的Nuxt 4全栈开发
前端·vue.js·ai-native
我是唐青枫10 小时前
Kotlin Delegation 委托详解
开发语言·kotlin
FRing0311 小时前
JavaScript入门到精通三十二(深拷贝、浅拷贝、递归函数)
开发语言·前端·javascript
excel11 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端