2021/06/20

[Jetpack Compose]ローディングアニメーションつきカードコンポーネントの作成

jetpack-composekotlin

概要

データローディング中に表示するカード形式のコンポーネントを作りました。

データを表示する箇所にグレーの背景を敷き、
ローディング中であることを分かりやすくするために、白いグラデーションが左から右に流れるように動くようにしました。

以下のような感じ(プレビューでの表示です)になります。

skeleton-card-min.png

注意事項

データ表示部のコンポーネントの作成

  • Brush.linearGradientを使って、一部白い線が入ったグラデーションの長方形を描画します
  • rememberinfinitetransitionを使って白い線が動くようにします。
@Composable
fun ShimmerRectangle(
    colors: List<Color> = listOf(
        Gray200,
        Gray50,
        Gray200,
    ),
    height: Dp = 225.dp
) {
    BoxWithConstraints {
        val widthPx = with(LocalDensity.current) { maxWidth.toPx() }
        val heightPx = with(LocalDensity.current) { height.toPx() }
        val gradientWidthPx = with(LocalDensity.current) {
            height.toPx() * 0.2f
        }
        val infiniteTransition = rememberInfiniteTransition()
        val animation = tween<Float>(durationMillis = 1300, delayMillis = 300, easing = LinearEasing)
        val xShimmer = infiniteTransition.animateFloat(
            initialValue = 0f,
            targetValue = widthPx + gradientWidthPx,
            animationSpec = infiniteRepeatable(
                animation = animation,
                repeatMode = RepeatMode.Restart
            )
        )
        val yShimmer = infiniteTransition.animateFloat(
            initialValue = 0f,
            targetValue = heightPx + gradientWidthPx,
            animationSpec = infiniteRepeatable(
                animation = animation,
                repeatMode = RepeatMode.Restart
            )
        )
        val brush = Brush.linearGradient(
            colors = colors,
            start = Offset(xShimmer.value - gradientWidthPx, yShimmer.value - gradientWidthPx),
            end = Offset(xShimmer.value, yShimmer.value),
        )
        Surface(
            shape = MaterialTheme.shapes.small
        ) {
            Spacer(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(height)
                    .background(brush = brush)
            )
        }
    }
}

表示用のカードを作成

  • 上で作成したアニメーション付きの長方形を複数組み合わせて、以下のように定義します。
@Composable
fun SkeletonCard() {
    val elevation = 8.dp
    Card(
        shape = MaterialTheme.shapes.medium,
        modifier = Modifier.fillMaxWidth(),
        backgroundColor = MyTheme.colors.getUiBackgroundForElevation(elevation = elevation),  // よりはっきりした白とか黒を取得しています。
        elevation = elevation
    ) {
        Column(
            modifier = Modifier.fillMaxWidth()
        ) {
            ShimmerRectangle(height = 225.dp)
            Spacer(modifier = Modifier.height(8.dp))
            Row(modifier = Modifier
                .fillMaxWidth()
                .padding(start = 16.dp, end = 60.dp)
            ) {
                ShimmerRectangle(height = 18.dp)
            }
            Spacer(modifier = Modifier.height(8.dp))
            Row(modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 16.dp)
            ) {
                ShimmerRectangle(height = 18.dp)
            }
            Spacer(modifier = Modifier.height(8.dp))
        }
    }
}

以上になります。