在 Jetpack Compose 中显示动画进度轮

问题描述 投票:0回答:1

在此 Google Codelab(火星照片)中,当 Coil 加载图像时,应用程序会显示进度轮。该代码使用此 XML 文件作为可绘制对象:

enter image description here

并使用此代码来显示它:

@Composable
fun LoadingScreen(modifier: Modifier = Modifier) {
    Box(
        contentAlignment = Alignment.Center,
        modifier = modifier.fillMaxSize()
    ) {
        Image(
            modifier = Modifier.size(200.dp),
            painter = painterResource(R.drawable.loading_img),
            contentDescription = stringResource(R.string.loading)
        )
    }
}

但是,这仅显示静态图像。我希望它旋转(我当然期望这是行为)。我想到了一些方法,但我不太喜欢:

  • 定义 12 个相似的 XML 文件,每个旋转度数一个,并连续按顺序显示每个文件
  • 动态生成 XML 并以某种方式将其即时转换为画家
  • 使用 Coil 加载 GIF

有没有更简单的方法来完成这项任务?有没有办法以编程方式旋转图像本身,例如应用某种预定义的动画模式(可能类似于 basicMarquee)?

android-jetpack-compose android-compose-image
1个回答
0
投票

这是相同的代码。此代码取自https://github.com/SmartToolFactory/Compose-ProgressIndicator,它还有许多其他旋转圆形进度视图动画。

@Composable
fun SpinningProgressIndicator(
    modifier: Modifier = Modifier,
    @androidx.annotation.IntRange(from = 4, to = 12) staticItemCount: Int = 12,
    dynamicItemCount: Int = staticItemCount / 2,
    staticItemColor: Color = StaticItemColor,
    dynamicItemColor: Color = DynamicItemColor,
    spinnerShape: SpinnerShape = SpinnerShape.RoundedRect,
    durationMillis: Int = 1000
) {

    // Number of rotating items
    val animatedItemCount = dynamicItemCount.coerceIn(1, staticItemCount)

    val coefficient = 360f / staticItemCount

    val infiniteTransition = rememberInfiniteTransition()
    val angle by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = staticItemCount.toFloat(),
        animationSpec = infiniteRepeatable(
            animation = tween(
                durationMillis = durationMillis,
                easing = LinearEasing
            ),
            repeatMode = RepeatMode.Restart
        )
    )

    Canvas(modifier = modifier
        .progressSemantics()
        .size(Size)
    ) {

        var canvasWidth = size.width
        var canvasHeight = size.height

        if (canvasHeight < canvasWidth) {
            canvasWidth = canvasHeight
        } else {
            canvasHeight = canvasWidth
        }

        val itemWidth = canvasWidth * .3f
        val itemHeight = canvasHeight / staticItemCount

        val cornerRadius = itemWidth.coerceAtMost(itemHeight) / 2

        val horizontalOffset = (size.width - size.height).coerceAtLeast(0f) / 2
        val verticalOffset = (size.height - size.width).coerceAtLeast(0f) / 2

        val topLeftOffset = Offset(
            x = horizontalOffset + canvasWidth - itemWidth,
            y = verticalOffset + (canvasHeight - itemHeight) / 2
        )

        val size = Size(itemWidth, itemHeight)

        // Stationary items
        for (i in 0..360 step 360 / staticItemCount) {
            rotate(i.toFloat()) {
                if (spinnerShape == SpinnerShape.RoundedRect) {
                    drawRoundRect(
                        color = staticItemColor,
                        topLeft = topLeftOffset,
                        size = size,
                        cornerRadius = CornerRadius(cornerRadius, cornerRadius)
                    )
                } else {
                    drawRect(
                        color = staticItemColor,
                        topLeft = topLeftOffset,
                        size = size,
                    )
                }
            }
        }

        // Dynamic items
        for (i in 0..animatedItemCount) {
            // angle is cast to into move in intervals of static items
            rotate((angle.toInt() + i) * coefficient) {
                if (spinnerShape == SpinnerShape.RoundedRect) {
                    drawRoundRect(
                        color = dynamicItemColor.copy(
                            alpha = (1f / dynamicItemCount * i).coerceIn(0f, 1f)
                        ),
                        topLeft = topLeftOffset,
                        size = size,
                        cornerRadius = CornerRadius(cornerRadius, cornerRadius)
                    )
                } else {
                    drawRect(
                        color = dynamicItemColor.copy(
                            alpha = (0.2f + 0.15f * i).coerceIn(
                                0f, 1f
                            )
                        ),
                        topLeft = topLeftOffset,
                        size = size,
                    )
                }
            }
        }
    }
}

val infiniteTransition = rememberInfiniteTransition()
用于无限过渡动画,RepeatMode.Restart 表示动画重新开始,给人一种无限动画的感觉。

rotate((angle.toInt() + i) * coefficient) {
旋转项目的位置。所有角度计算均以弧度为单位。

您可以根据您的需求进行自定义。你不需要图像。绘制线条或矩形,然后通过旋转它们来制作其中的一些动画。

© www.soinside.com 2019 - 2024. All rights reserved.