🛣️

LazyList における offset とは

2023/11/14に公開

JetpackCompose の LazyList において、LazyListState から様々な情報が取得できます。
そのうち、以下のようにすると各アイテムの offset という値が取得できます。

val lazyListState = rememberLazyListState()
lazyListState.layoutInfo.visibleItemsInfo.map { it.offset }

この値の説明は以下のようになっています。

The main axis offset of the item in pixels. It is relative to the start of the lazy list container.

すなわち、親となる LazyList の start 位置からの相対位置を表しています。
例えば、図 1 は実装例を示します。
このとき、「Item 1」の offset は「Item 0」の高さと一致します。

normal
図 1

これを用いたりして頑張ることで Scrollbar の自前実装ができます。
しかし、LazyList の設定値次第では一見不思議な値を取るため、二点紹介します。

offset と contentPadding

contentPaddingLazyColumnLazyRow の引数の一つで、リスト全体に対する padding を指定できます。
図 2 は contentPaddingPaddingValues(vertical = 40.dp) を指定した際の例を示します。

@Composable
fun LazyColumn(
    ...
    contentPadding: PaddingValues = PaddingValues(0.dp),
    ...
): Unit

contentPadding
図 2: contentPadding = PaddingValues(vertical = 40.dp) を指定した場合

図 1 と図 2 における各アイテムの offset を以下に示します。

(Item 0).offset (px) (Item 1).offset (px)
1 0 163
2 0 163
2 (スクロールした場合) -105 58

ここで、(Item 0).size は 163px で、40dp は 105px でした。
図 1 と図 2 で数値に変化が見られませんが、contentPaddingoffset に影響しない、というわけではありません。
行「2 (スクロールした場合)」は図 2 を図 1 と見た目が同じになるところまでスクロールした場合の各アイテムの offset を示しています。
これを見ると、Item 0, Item 1 ともに 40dp (105px) 分減少しています。
この結果から、図 2 では offset は親の上端を基準とし、contentPadding を各アイテムに適用したときの上端の位置を表している、と言えそうです。
上記から明らかですが、offset には contentPadding のうち top のみが作用します。

offset と reverseLayout

reverseLayoutLazyColumnLazyRow の引数の一つで、true を指定するとレイアウトやスクロール方向を逆転させます。
図 3 は reverseLayouttrue とした際の例を示します。

@Composable
fun LazyColumn(
    ...
    reverseLayout: Boolean = false,
    ...
): Unit

reverseLayout
図 3: reverseLayout = true を指定した場合

図 1 と図 3 における各アイテムの offset を以下に示します。

(Item 0).offset (px) (Item 1).offset (px)
1 0 163
3 0 163

ここで、(Item 0).size は 163px でした。
またもや数値に変化が見られませんが、今回も reverseLayoutoffset に影響しない、というわけではありません。
ここで図 3 において Item 0 が隠れるようにスクロールをすると、各アイテムの offset は減少します。
つまり、図 1 では「親の上端」を基準としたときの「各アイテムの上端の位置」を表しているのに対し、図 3 では「親の下端」を基準としたときの「各アイテムの下端の位置」を表していると言えそうです。

なお、ここで更に contentPadding を指定することもできます。
このとき offset は「各アイテムの下端の位置」を表しているので、contentPadding のうち bottom のみが作用します。

Discussion