LazyColumnとColumnはどちらも一覧(リスト)表示するUIです。
記述方法は違いますが、スクロールしたり、アイテムを制御したり、ほぼ同じことが出来ます。
ただし、両者の特徴により、向き不向きがあります。よく違いを理解して、適材適所で使い分けが必要です。
ここでは、両者の違いをまとめました。
※環境:Android Studio Koala | 2024.1.1
Kotlin 1.9.0
Compose Compiler 1.5.1
androidx.compose.foundation:foundation 1.6.8
Columnとの違い
LazyColumnは、「複数のデータやオブジェクト」または「コレクション」を、一覧表示するUIです。
同様なことがColumnでも出来ます。しかし、表に示すような特徴があります。
| 評価項目 | Column | LazyColumn | ||
|---|---|---|---|---|
| (1) | 記述の容易さ | |||
| (2) | パフォーマンス | 小規模リスト(表示範囲+α程度) | ||
| 大規模リスト | ||||
| (3) | アイテムの制御 ※制御:変更、追加、削除、移動 | |||
| ※◎:とても適する、○:適する、△:あまり適しない、×:適しない | ||||
適材適所で使い分けが必要です。
記述の容易さ
LazyColumnはDSLを用いてアイテムの並びを記述します。ですので、DSLの理解が必要です。
※各DSLの詳細は「Compose LazyColumn:LazyListScope DSL」を参照
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
headerItem()
playerList.forEach {
playerItem(it.name, it.level, it.score)
}
}
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
@OptIn(ExperimentalFoundationApi::class)
stickyHeader { // DSLコマンド
headerItem()
}
items(playerList) { item -> // DSLコマンド
playerItem(item.name, item.level, item.score)
}
}
両者は同じ表示になります。スクロールも可能です。

パフォーマンス
小規模リストはColumn、大規模リストはLazyColumnが適しています。
ColumnColumnは初回のコンポーズで、すべてのアイテムをComposition内に展開します。これは、1000個のアイテムがあれば1000個のUIが展開される事を意味します。
ですので、アイテムが多くなればなるほど、端末のリソース(メモリーやCPU処理能力)をより多く消費し、その影響でパフォーマンスは低下します。
スクロールは、Composition内のアイテム上をウィンドウが移動するイメージです。

LazyColumnは初回のコンポーズで、表示分のアイテムをComposition内に展開します。サンプルは、6個のアイテムが表示可能なので、6個のUIが展開されています。
ですので、アイテムが多くなったとしても、端末のリソースの消費量は一定で、パフォーマンスの低下は起こりません。
スクロールは、スクロール先のUIを事前準備する関係で、Composition内のアイテム数が多少増加しますが、表示可能なアイテム数のx1.8~2.0程度です。その後は、既存のアイテムを使い回し、数は変化しません。

アイテムの制御
ColumnもLazyColumnもアイテムの制御(変更/追加/移動/削除)は可能です。また、不要な再Composeを排除するために、アイテムをキーで認識することも可能です。
ただし、LazyColumnのキーはLazyListScope DSL内に組み込まれているので、個別にkey関数を使う必要がありません。
@Stable
data class NumData(val id: Int, var num: Int)
private var ID: Int = 100
private fun newData() = try { NumData(ID, ID) } finally { ID++ }
@Composable
private fun SampleList_a(datas: List<NumData>) {
val _datas = remember {
mutableStateListOf<NumData>().apply { addAll(datas) }
}
Column {
_datas.forEach {
key(it.id) {
ItemBox { Text(text = "Item %02d (Column)".format(it.num)) }
}
}
}
Button(
onClick = { _datas.add(4, newData()) }
) { Text(text = "ins")}
}
@Stable
data class NumData(val id: Int, var num: Int)
private var ID: Int = 100
private fun newData() = try { NumData(ID, ID) } finally { ID++ }
@Composable
private fun SampleList_1(datas: List<NumData>) {
val _datas = remember {
mutableStateListOf<NumData>().apply { addAll(datas) }
}
LazyColumn {
items(items = _datas, key = { it.id }) {
ItemBox { Text(text = "Item %02d".format(it.num)) }
}
}
Button(
onClick = { _datas.add(4, newData()) },
) { Text(text = "ins")}
}
※DSLの詳細は「Compose LazyColumn:LazyListScope DSL」を参照
※「アイテムの制御」の詳細は「… LazyColumn:アイテムの制御(Ch…)」を参照
関連記事:
