Jetpack Compose:如何创建评级栏?

Jar*_*ojr 4 android kotlin android-jetpack-compose

我正在尝试实施评级栏。我参考https://gist.github.com/vitorprado/0ae4ad60c296aefafba4a157bb165e60但我不明白这段代码中的任何内容。它有效,但当我使用此代码时,星星没有圆角。我想实现如下所示的东西:

Thr*_*ian 16

我为此制作了非常基本的示例,它将给出创建带有示例边框和填充 png 文件的评级栏的基本想法。

@Composable
private fun RatingBar(
    modifier: Modifier = Modifier,
    rating: Float,
    spaceBetween: Dp = 0.dp
) {

    val image = ImageBitmap.imageResource(id = R.drawable.star)
    val imageFull = ImageBitmap.imageResource(id = R.drawable.star_full)

    val totalCount = 5

    val height = LocalDensity.current.run { image.height.toDp() }
    val width = LocalDensity.current.run { image.width.toDp() }
    val space = LocalDensity.current.run { spaceBetween.toPx() }
    val totalWidth = width * totalCount + spaceBetween * (totalCount - 1)


    Box(
        modifier
            .width(totalWidth)
            .height(height)
            .drawBehind {
                drawRating(rating, image, imageFull, space)
            })
}

private fun DrawScope.drawRating(
    rating: Float,
    image: ImageBitmap,
    imageFull: ImageBitmap,
    space: Float
) {

    val totalCount = 5

    val imageWidth = image.width.toFloat()
    val imageHeight = size.height

    val reminder = rating - rating.toInt()
    val ratingInt = (rating - reminder).toInt()

    for (i in 0 until totalCount) {

        val start = imageWidth * i + space * i

        drawImage(
            image = image,
            topLeft = Offset(start, 0f)
        )
    }

    drawWithLayer {
        for (i in 0 until totalCount) {
            val start = imageWidth * i + space * i
            // Destination
            drawImage(
                image = imageFull,
                topLeft = Offset(start, 0f)
            )
        }

        val end = imageWidth * totalCount + space * (totalCount - 1)
        val start = rating * imageWidth + ratingInt * space
        val size = end - start

        // Source
        drawRect(
            Color.Transparent,
            topLeft = Offset(start, 0f),
            size = Size(size, height = imageHeight),
            blendMode = BlendMode.SrcIn
        )
    }
}

private fun DrawScope.drawWithLayer(block: DrawScope.() -> Unit) {
    with(drawContext.canvas.nativeCanvas) {
        val checkPoint = saveLayer(null, null)
        block()
        restoreToCount(checkPoint)
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

Column {
    RatingBar(rating = 3.7f, spaceBetween = 3.dp)
    RatingBar(rating = 2.5f, spaceBetween = 2.dp)
    RatingBar(rating = 4.5f, spaceBetween = 2.dp)
    RatingBar(rating = 1.3f, spaceBetween = 4.dp)
}
Run Code Online (Sandbox Code Playgroud)

结果

在此输入图像描述

还创建了一个使用手势、其他 png 文件和矢量作为评级项目的库,可在此处找到。

@Composable
fun RatingBar(
  modifier: Modifier = Modifier,
  rating: Float,
  painterEmpty: Painter,
  painterFilled: Painter,
  tintEmpty: Color? = DefaultColor,
  tintFilled: Color? = null,
  itemSize: Dp = Dp.Unspecified,
  rateChangeMode: RateChangeMode = RateChangeMode.AnimatedChange(),
  gestureMode: GestureMode = GestureMode.DragAndTouch,
  shimmer: Shimmer? = null,
  itemCount: Int = 5,
  space: Dp = 0.dp,
  ratingInterval: RatingInterval = RatingInterval.Unconstrained,
  allowZeroRating: Boolean = true,
  onRatingChangeFinished: ((Float) -> Unit)? = null,
  onRatingChange: (Float) -> Unit
)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述