LoginSignup
0
0

LazyListで謎のコンポジションが発生する

Last updated at Posted at 2023-06-17

はじめに

原因も解決策もわかってません。
ただそういう事象が発生するという共有です。

本題

LazyColumn, LazyRowどちらも、画面内にない要素のコンポジションが発生します。

たとえばこういうシンプルなLazyColumnがあったとして

LazyColumn {
   items(100) { i ->
      Box(
         Modifier
            .fillParentMaxWidth()
            .height(48.dp)
      ) {
         Text("$i")
      }
   }
}

画面の一番上に10、一番下に40が表示されていたとします。

こういう状態です。

  |-------------|
  | 9           |
  |             |
  |             |
  |-------------|
  | 10          |
+-----------------+
|                 |
|-----------------|
|   11            |
|                 |
|                 |
|-----------------|
|                 |
|        :        |
|        :        |
|                 |
|-----------------|
|   39            |
|                 |
|                 |
|-----------------|
|   40            |
+-----------------+
  |             |
  |-------------|
  | 41          |
  |             |
  |             |
  |-------------|

このとき、9と41が高頻度でコンポーズされることがあります。

下(41がある方向)にスクロールするとき41がコンポーズされて上(9がある方向)にスクロールするとき9がコンポーズされます。
つまり発生条件は『上下に細かくスクロールしたとき』なので、最も基本的なスクロールをする使い方だけだと発生しません。
スクロール中に指を止めたときに発生することはあります。人間の指は止めているつもりでも細かく震えていますからね。

ちなみにCompose for Desktopでは発生しません。 Androidだけです。 いやiOSとWebは試してないのでもしかしたらAndroidだけではないかもしれないです。とりあえずDesktopでは発生してませんでした。

このコンポジションは スキップされません。
たとえ引数に何も受け取らなくても、です。

LazyColumn {
   items(100) {
      Content()
   }
}

@Composable
private fun Content() {
   Box(
      Modifier
         .fillMaxWidth()
         .height(48.dp)
   ) {
      SideEffect {
         Log.i("Composed")
      }

      Text("Text")
   }
}

たぶんリコンポジションではないんだろうなと思って先程から「コンポジション」と表記しているわけです。

この例ではLazyColumnを使いましたがLazyRowでも同様の現象が発生します。
内部的にLazyLayoutを利用しているHorizontalPagerとVerticalPagerも同様です。

原因

わからん。

ソースコード的には画面内にある要素しかコンポジションしてないしデバッガで止めてみても実際画面内しかコンポジションしてないように見える。
そしてLazyLayoutのコードがデカすぎて追い切れず諦めました。

対策

LazyListの要素のコンポジションが重くなること自体があまりないからそこまで気にしなくてもいいのかもしれません。
というか、話題になっていないあたり、実際問題にならないことが多いのでしょう。
ただPager系は重くなる可能性もあるので気にしておいたほうがいいかも?

当たり前ですが remember していようが再実行されますので重い処理を remember しておく対策はここでは使えません。
やるとしたらLazyList外でrememberすることになります。

危ない書き方

HorizontalPager(
   pageCount = 2
) { page ->
   when (page) {
      0 -> {
         Page0(list)
      }
      1 -> {
         Page1()
      }
   }
}

@Composable
private fun Page0(
   list: ImmutableList<Hoge>
) {
   val sortedList = remember(list) { list.sorted() }
}

比較的安全な書き方

+ val sortedList = remember(list) { list.sorted() }

  HorizontalPager(
     pageCount = 2
  ) { page ->
     when (page) {
        0 -> {
-          Page0(list)
+          Page0(sortedList)
        }
        1 -> {
           Page1()
        }
     }
  }

  @Composable
  private fun Page0(
-    list: ImmutableList<Hoge>
+    sortedList: ImmutableList<Hoge>
  ) {
-    val sortedList = remember(list) { list.sorted() }
  }

まとめ

  • LazyListはユーザーの操作次第で要素が高頻度でコンポジションされうる
  • LazyListを利用しているPager系も同様
  • このコンポジションはスキップされない
  • 要素内で重い処理をするのはなるべく避けよう。rememberしても無駄なので、重い処理は可能ならLazyList外でやる方がいい
0
0
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
0
0