如何使用 Jetpack Compose 动画创建定时 Instagram 故事加载栏?

Tri*_*ity 3 android android-animation android-layout android-jetpack-compose

我想创建一个与 Instagram 故事加载栏非常相似的可组合组件,持续时间为 10 秒。

在此输入图像描述

我有一个想法如何去做,但我不知道如何执行。我正在考虑使用 BOX 添加一个静态栏(灰色),然后添加另一个栏(白色),该栏在 10 秒内从 0 动画到最终可组合宽度。

您知道我如何实现这个组件吗?

Fra*_*esc 6

您可以使用此可组合项创建分段进度条

private const val BackgroundOpacity = 0.25f
private const val NumberOfSegments = 8
private val StrokeWidth = 4.dp
private val SegmentGap = 8.dp

@Composable
fun SegmentedProgressIndicator(
    /*@FloatRange(from = 0.0, to = 1.0)*/
    progress: Float,
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colors.primary,
    backgroundColor: Color = color.copy(alpha = BackgroundOpacity),
    strokeWidth: Dp = StrokeWidth,
    numberOfSegments: Int = NumberOfSegments,
    segmentGap: Dp = SegmentGap
) {
    val gap: Float
    val stroke: Float
    with(LocalDensity.current) {
        gap = segmentGap.toPx()
        stroke = strokeWidth.toPx()
    }
    Canvas(
        modifier
            .progressSemantics(progress)
            .fillMaxWidth()
            .height(strokeWidth)
            .focusable()
    ) {
        drawSegments(1f, backgroundColor, stroke, numberOfSegments, gap)
        drawSegments(progress, color, stroke, numberOfSegments, gap)
    }
}

private fun DrawScope.drawSegments(
    progress: Float,
    color: Color,
    strokeWidth: Float,
    segments: Int,
    segmentGap: Float,
) {
    val width = size.width
    val start = 0f
    val gaps = (segments - 1) * segmentGap
    val segmentWidth = (width - gaps) / segments
    val barsWidth = segmentWidth * segments
    val end = barsWidth * progress + (progress * segments).toInt()* segmentGap

    repeat(segments) { index ->
        val offset = index * (segmentWidth + segmentGap)
        if (offset < end) {
            val barEnd = (offset + segmentWidth).coerceAtMost(end)
            drawLine(
                color,
                Offset(start + offset, 0f),
                Offset(barEnd, 0f),
                strokeWidth
            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你这样使用它

var running by remember { mutableStateOf(false) }
val progress: Float by animateFloatAsState(
    if (running) 1f else 0f,
    animationSpec = tween(
        durationMillis = 10_000,
        easing = LinearEasing
    )
)
Surface(color = MaterialTheme.colors.background) {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        SegmentedProgressIndicator(
            progress = progress,
            modifier = Modifier
                .padding(top = 64.dp, start = 32.dp, end = 32.dp)
                .fillMaxWidth(),
        )

        Button(
            onClick = { running = !running },
            modifier = Modifier.padding(top = 32.dp)
        ) {
            Text(
                text = if (running) "Reverse Animation" else "Start Animation"
            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是结果

在此输入图像描述