4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ZOZOAdvent Calendar 2024

Day 20

Jetpack Composeで特定要素での遅延読み込みを実装する

Posted at

やりたいこと

LazyColumnで複数の要素を表示する際に、特定の要素が表示されたタイミングで追加のデータが読み込まれるようにします。これにより、初期表示を高速化したり不要なAPI呼び出しを制限することでパフォーマンスの向上を目指します。

LazyColumnの実装

まず、LazyColumnで表示するデータの準備を行います。今回は色とりどりなコンテンツを表示するために、sealed classでdata objectを定義します。

sealed class ContentType {
    data object Header : ContentType()
    data object Red : ContentType()
    data object Yellow : ContentType()
    data object Blue : ContentType()
    data object Green : ContentType()
    data object Magenta : ContentType()
    data object Cyan : ContentType()
    data object LoadingIndicator : ContentType()
}

次に、表示するコンテンツを格納するためのmutableStateListを作成します。

val contentList = remember {
    mutableStateListOf(
        ContentType.Header,
        ContentType.Red,
        ContentType.Yellow,
        ContentType.Blue,
        ContentType.Green
    )
}

CntentTypeに対応したComposableも実装して、以下のように分岐してそれぞれのComposableが表示されるようなLazyColumnを作成します。

LazyColumn(
    modifier = Modifier
        .fillMaxWidth()
        .fillMaxHeight(),
    verticalArrangement = Arrangement.spacedBy(20.dp),
) {
    items(contentList) { contentType ->
        when (contentType) {
            is ContentType.Header -> HeaderContent()
            is ContentType.Red -> RedContent()
            is ContentType.Yellow -> YellowContent()
            is ContentType.Blue -> BlueContent()
            // 省略
        }
    }
}

遅延読み込みの実装

以下の手順で遅延読み込みを実装します。

1. rememberLazyListState()を定義する

LazyColumnのスクロール状態を管理するために、 rememberLazyListState()を使用してLazyListStateオブジェクトを作成します。

2. LazyColumnのstateに渡す

作成したLazyListStateオブジェクトを、LazyColumnのstateパラメータに渡します。これにより、LazyColumnのスクロール状態を監視できるようになります。

3. listState.layoutInfo.visibleItemsInfoをsnapshotFlowで受け取る

listState.layoutInfo.visibleItemsInfoで現在画面に表示されているアイテムの情報を受け取ることができます。ただ、visibleItemsInfoは頻繁に更新される可能性があるため、不要な再コンポジションを発生させる可能性があります。そのため、snapshotFlowを使用してlistState.layoutInfo.visibleItemsInfoを監視します。snapshotFlowを使用することで状態の変化をストリームとして受け取ることができます。
この監視とそれに伴う遅延読み込み処理は、Composable関数の外部で実行するためにLaunchedEffect内で実行します。

4. 読み込み開始したいContentTypeで読み込み始める

visibleItemsInfoから読み込みを開始したいContentTypeが表示されているかどうかを判定します。今回はContentType.Greenに対応する要素が表示された時に読み込み処理を開始するようにしています。読み込み処理にかんしては、5秒後に次に読み込む要素を返す関数を呼び出しています。

5. 無限読み込みを回避するためにisLoadingで制御する

読み込み処理が複数回実行されないように、isLoadingフラグを使用して制御します。読み込み処理が開始されたらisLoadingをtrueに設定し、読み込みが完了したらfalseに戻します。

ここまででコードは以下のようになります。

// 1. rememberLazyListState()を定義する
val listState = rememberLazyListState()
// 5. 無限読み込みを回避するためにisLoadingで制御する
var isLoading by remember { mutableStateOf(false) }

LaunchedEffect(key1 = Unit) {
    snapshotFlow { listState.layoutInfo.visibleItemsInfo }
        .collect { visibleItemsInfo ->
            // 4. 読み込み開始したいContentTypeで読み込み始める
            if (visibleItemsInfo.any { contentList[it.index] is ContentType.Green } && !isLoading) {
                isLoading = true
                contentList.add(ContentType.LoadingIndicator) // ロードインジケーターを追加
                // ここで、非同期で次のアイテムを読み込む処理を実行
                val newItems = loadMoreItems() 
                contentList.remove(ContentType.LoadingIndicator) // ロードインジケーターを削除
                contentList.addAll(newItems) // 新しいアイテムを追加
            }
        }
}

LazyColumn(
    modifier = Modifier
        .fillMaxWidth()
        .fillMaxHeight(),
    verticalArrangement = Arrangement.spacedBy(20.dp),
    state = listState // 2. LazyColumnのstateに渡す
) {
    // 省略
}

// 次のアイテムを読み込む関数
suspend fun loadMoreItems(): List<ContentType> {
    delay(5000)

    return listOf(
        ContentType.Magenta,
        ContentType.Cyan,
    )
}

結果

緑の要素が表示された時に読み込みが開始されて次の要素が表示されるようになりました🙌
LazyLoad_sample.gif

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?