疑問
次のコードは、表示されている要素のインデックスの中で最も小さいものをログに出力するコードです。出力されるタイミングは値が変わったとき、つまり一方向にゆっくりと 64dp
スクロールしたときです。私の環境では 1dp = 2.75px
であるため、176px
毎に出力されることになります。
data class Data(val id: Int)
val data = (1..1000).map { Data(it) }
@Composable
fun MainScreen() {
val state = rememberLazyListState()
Log.e("TAG", state.firstVisibleItemIndex.toString())
LazyColumn(Modifier.fillMaxSize(), state = state) {
items(data, key = { it.id }) {
Box(Modifier.border(1.dp, Color.Black).fillMaxWidth().height(64.dp))
}
}
}
それに対して次のコードは、1px
スクロールするたびに出力されます。
data class Data(val id: Int)
val data = (1..1000).map { Data(it) }
@Composable
fun MainScreen() {
val state = rememberLazyListState()
// ここだけを書き換えている
Log.e("TAG", state.firstVisibleItemScrollOffset.toString())
LazyColumn(Modifier.fillMaxSize(), state = state) {
items(data, key = { it.id }) {
Box(Modifier.border(1.dp, Color.Black).fillMaxWidth().height(64.dp))
}
}
}
私の感覚では、state
変数のプロパティの一部だけを参照していようとも、1px
スクロールする毎に firstVisibleItemScrollOffset
の値が変わるのだから state
自身が変更されると思ってしまいます。しかし実際はもっと賢くて、firstVisibleItemIndex
だけを参照しているときは 176px
毎に再描画が走ってくれます。firstVisibleItemScrollOffset
が変更されているにも関わらず。
答え
rememberLazyListState
の実装を読むと答えは単純でした。この関数は State#value
を返すのではなく、State#value
をプロパティに持つ State#value
でないオブジェクトを返していました。つまり、state.firstVisibleItemIndex
や state.firstVisibleItemScrollOffset
は State#value
ですが、state
は State#value
ではありませんでした。
終わりに
参照しているプロパティが変更されたときだけ再描画が行われるので、「スクロールするたびに再描画が走るので rememberLazyListState
を使うことは避けよう」と思わなくていいとわかり、安心しました。