在此 Google Codelab(火星照片)中,当 Coil 加载图像时,应用程序会显示进度轮。该代码使用此 XML 文件作为可绘制对象:
并使用此代码来显示它:
@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)
)
}
}
但是,这仅显示静态图像。我希望它旋转(我当然期望这是行为)。我想到了一些方法,但我不太喜欢:
有没有更简单的方法来完成这项任务?有没有办法以编程方式旋转图像本身,例如应用某种预定义的动画模式(可能类似于 basicMarquee)?
这是相同的代码。此代码取自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) {
旋转项目的位置。所有角度计算均以弧度为单位。
您可以根据您的需求进行自定义。你不需要图像。绘制线条或矩形,然后通过旋转它们来制作其中的一些动画。