组件介绍
纯 Jetpack Compose 实现的图片版星星评分组件,无自定义绘制、无形状错误、分数精准,支持点击评分、滑动评分、半星显示、自定义样式、只读模式,兼容所有 Compose 正式版。
功能特性
- ✅ 使用本地星星图片(实心/半星/空心,形状完美)
- ✅ 精准点击评分 + 流畅滑动评分
- ✅ 支持半星显示(可开关)
- ✅ 自定义星星数量、大小、间距
- ✅ 支持只读模式(仅展示不可交互)
- ✅ 分数自动限制 0 ~ 最大星星数
- ✅ 外部传入分数实时同步
- ✅ 无
toPx()报错,兼容所有 Compose 版本
前置准备
将 3 张星星图片放入 res/drawable 目录:
ic_star_full:实心星星(选中状态)ic_star_half:半星(可选)ic_star_empty:空心星星(未选中状态)
无半星图片时,设置
enableHalfStar = false即可。
完整代码
kotlin
import androidx.compose.foundation.Image
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlin.math.ceil
import kotlin.math.roundToInt
// 星星类型枚举
private enum class StarType {
FULL, HALF, EMPTY
}
/**
* 图片版精准星星评分组件
* @param score 当前评分
* @param stars 总星星数量
* @param starSize 单个星星大小
* @param space 星星间距
* @param enableHalfStar 是否支持半星显示
* @param onScoreChange 评分变更回调
* @param enabled 是否可交互(false=只读展示)
* @param fullStar 实心星星图片资源
* @param halfStar 半星图片资源
* @param emptyStar 空心星星图片资源
*/
@Composable
fun StarRatingBar(
modifier: Modifier = Modifier,
score: Float = 0f,
stars: Int = 5,
starSize: Dp = 28.dp,
space: Dp = 6.dp,
enableHalfStar: Boolean = true,
onScoreChange: (Float) -> Unit = {},
enabled: Boolean = true,
fullStar: Int = R.drawable.ic_star_full,
halfStar: Int = R.drawable.ic_star_half,
emptyStar: Int = R.drawable.ic_star_empty
) {
val density = LocalDensity.current
val singleStarTotalWidth = with(density) { (starSize + space).toPx() }
// 内部评分状态,同步外部传入值
var currentScore by remember { mutableFloatStateOf(score.coerceIn(0f, stars.toFloat())) }
// 监听外部分数变化,自动同步
LaunchedEffect(score) {
currentScore = score.coerceIn(0f, stars.toFloat())
}
// 精准计算评分
val calculateScore: (Offset) -> Unit = { offset ->
val rawScore = offset.x / singleStarTotalWidth
val newScore = if (enableHalfStar) {
(rawScore * 2).roundToInt() / 2f
} else {
ceil(rawScore)
}.coerceIn(0f, stars.toFloat())
currentScore = newScore
onScoreChange(newScore)
}
// 评分交互布局
Row(
modifier = modifier
.pointerInput(enabled) {
if (enabled) detectTapGestures { calculateScore(it) }
}
.pointerInput(enabled) {
if (enabled) detectDragGestures { change, _ ->
calculateScore(change.position)
}
},
horizontalArrangement = Arrangement.spacedBy(space)
) {
for (index in 1..stars) {
val starType = when {
currentScore >= index -> StarType.FULL
currentScore >= index - 0.5f -> StarType.HALF
else -> StarType.EMPTY
}
val icon = when (starType) {
StarType.FULL -> fullStar
StarType.HALF -> halfStar
StarType.EMPTY -> emptyStar
}
Image(
painter = painterResource(id = icon),
contentDescription = null,
modifier = Modifier.size(starSize)
)
}
}
}
使用示例
kotlin
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun StarRatingDemo() {
// 评分状态
var userScore by remember { mutableFloatStateOf(3.5f) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(20.dp)
) {
// 显示当前分数
Text(
text = "当前评分:$userScore 分",
modifier = Modifier.padding(bottom = 12.dp)
)
// 标准评分组件(支持半星)
StarRatingBar(
score = userScore,
onScoreChange = { userScore = it },
enableHalfStar = true
)
// 自定义样式:大星星、红色、只读、无半星
StarRatingBar(
modifier = Modifier.padding(top = 20.dp),
score = 4f,
starSize = 32.dp,
enableHalfStar = false,
enabled = false // 只读模式
)
}
}
常用参数说明
| 参数名 | 作用 | 默认值 |
|---|---|---|
| score | 外部传入的当前评分 | 0f |
| stars | 总星星数量 | 5 |
| starSize | 单个星星大小 | 28.dp |
| space | 星星之间的间距 | 6.dp |
| enableHalfStar | 是否开启半星显示 | true |
| enabled | 是否可点击/滑动评分 | true |
| onScoreChange | 评分变化回调 | - |
| fullStar | 实心星星图片 | R.drawable.ic_star_full |
| emptyStar | 空心星星图片 | R.drawable.ic_star_empty |