😎

ComposableのPreviewで下が見切れてしまう時の対応

2023/12/16に公開

こちらの記事はGENDA Advent Calendar 2023 の17日目の記事です!

ComposableのPreview便利ですよね!

アプリを実装していく中で、画面や処理の状態によって、
オブジェクトの表示を変えることがありますよね?

状態が、4~5くらいであれば、管理も簡単ですが、
10個以上になってしまうことも少なくありません。

ComposeはPreview機能が充実しています。
1つの部品に対して、複数の状態のPreviewを用意できます。
下記のように1画面内で、複数の状態を確認できて、非常に便利です。


(画像はKotlinのマスコットKodee君です。)

表示する要素数が多くなければ、
下記のようにループしてPreviewを書くことが多いのではないでしょうか?

@Preview
@Composable
fun KodeeDisPlayPreview() {
    Column {
        KodeeState.entries.forEach {
            KodeeDisplay(state = it)
        }
    }
}

要素数 == 多い && 十分な高さを持つ場合は・・・?

サンプルとして、18個の要素を持つEnum Classを作成しました。

enum class Type {
    NORMAL,
    FIRE,
    WATER,
    GRASS,
    ELECTRIC,
    ICE,
    FIGHTING,
    POISON,
    GROUND,
    FLYING,
    PSYCHIC,
    BUG,
    ROCK,
    GHOST,
    DRAGON,
    DARK,
    STEEL,
    FAIRY
}

そして、このTypeに応じて、表示を変えるComposable関数を用意しました。
Typeによって、背景の色とテキストが変わる簡単なComposable関数です。
高さは80dpで固定しています。

fun TypeDisplay(type: Type) {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .height(80.dp)
            .clip(RoundedCornerShape(25.dp))
            .background(getTypeColor(type))
    ) {
        Text(
            text = stringResource(getTypeText(type)),
            modifier = Modifier.align(Alignment.Center),
            fontSize = 20.sp,
            color = Color.White,
            textAlign = TextAlign.Center
        )
    }
}

Previewのコードは下記のように書きました。
ループで書いているので、新しいTypeが追加されても安心ですね!

@Preview
@Composable
fun PreviewTypeDisplay() {
    Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
        Type.entries.forEach {
            TypeDisplay(it)
        }
    }
}

それでは、Previewを見てみましょう!

18個表示されるはずが、10個しか表示されません。
また10個目の「ひこう」は意図した高さを持っていないように見えます。

原因

Previewがデフォルトで1000dpまでしか大きくならないようです。

解決策1 Previewの高さを指定

Previewの高さを明示的に指定することが出来ます。

@Preview(heightDp = 2000)
@Composable
fun PreviewTypeDisplay() {
    Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
        Type.entries.forEach {
            TypeDisplay(it)
        }
    }
}

これでも良いのですが、要素の追加などで
さらに高さが必要になった際に、追加修正が必要になる恐れがあります。

解決策2 PreviewParameterProviderを利用する

PreviewParameterProviderを用意して、引数として指定すると良いです。
valuesの数だけ、Previewが生成されます。

class PreviewGameControlButtonProvider : PreviewParameterProvider<Type> {
    override val values: Sequence<Type>
        get() = Type.entries.asSequence()
}

@Preview()
@Composable
fun PreviewTypeDisplay(
    @PreviewParameter(PreviewGameControlButtonProvider::class) type: Type
) {
    TypeDisplay(type = type)
}

下記のようなPreviewが表示されました。

画面キャプチャの関係で、「じめん」までしか写っていませんが、
スクロールすると18個全てが正常な大きさで表示されます。

個人的にはコチラをオススメします。

最後に

PreviewParameterProviderは、Enumの要素以外でも利用できますし、
適切なPreviewをより簡潔に書けるので、個人的には好みでした。

Previewを見る機会は、実装時だけでなく、コードレビューの際などにも利用することもあります。
そのため、プロダクトの品質向上やレビューコストの削減のためにも
意図しないPreviewが表示されてしまう事態は無くしていきたいと思いました。(小並感)

Discussion