Compose笔记(十四)--LazyColumn
这一节了解一下Compose中的LazyColumn,在Jetpack Compose 中,LazyColumn 是一个用于高效显示长列表或可滚动垂直布局的组件。它类似于传统 Android 开发中的 RecyclerView,但专为 Compose 的声明式 UI 框架设计,能够显著优化性能,特别是在处理大量数据时。
特点
1 惰性加载(Lazy Loading)
LazyColumn 仅渲染当前屏幕上可见的列表项,未显示的部分不会被加载,从而减少内存占用和初始化时间。
2 高效性能
通过按需渲染和复用已加载的组件,LazyColumn 在处理大数据集时表现优异,避免了传统布局(如 Column)因一次性加载所有项而导致的性能问题。
3 声明式 API
使用 Compose 的声明式语法,开发者可以通过简单的代码定义列表项的布局和行为,无需手动管理适配器或视图持有者(ViewHolder)。
与RecyclerView的对比
LazyColumn一般场景
长列表或大数据集:如聊天记录、商品列表、新闻 feed 等。
动态内容更新:需要频繁更新列表项的场景。
复杂布局:需要嵌套其他 Compose 组件的列表。
栗子
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
//显示简单列表
@Composable
fun SimpleListExample() {
val itemsList = listOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")
LazyColumn {
items(itemsList) { item ->
Text(text = item)
}
}
}
// 键值(Key)支持
//通过为每个项指定唯一的键值,可以优化列表更新时的性能。
items(items, key = { it.id }) { item ->
Text(text = item.name)
}
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
//带有头和尾的列表
@Composable
fun ListWithHeaderAndFooterExample() {
val itemsList = listOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")
LazyColumn {
item {
Text(text = "Header", modifier = Modifier.padding(16.dp))
}
items(itemsList) { item ->
Text(text = item, modifier = Modifier.padding(16.dp))
}
item {
Text(text = "Footer", modifier = Modifier.padding(16.dp))
}
}
}
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
sealed class ListItem {
data class TextItem(val text: String) : ListItem()
data class HeaderItem(val title: String) : ListItem()
}
//不同类型的列表项
@Composable
fun ListWithDifferentItemTypesExample() {
val itemsList = listOf(
ListItem.HeaderItem("Section 1"),
ListItem.TextItem("Item 1"),
ListItem.TextItem("Item 2"),
ListItem.HeaderItem("Section 2"),
ListItem.TextItem("Item 3"),
ListItem.TextItem("Item 4")
)
LazyColumn {
items(itemsList) { item ->
when (item) {
is ListItem.HeaderItem -> {
Text(text = item.title, modifier = Modifier.padding(16.dp))
}
is ListItem.TextItem -> {
Text(text = item.text, modifier = Modifier.padding(16.dp).padding(start = 32.dp))
}
}
}
}
}
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
//分页加载列表
@Composable
fun PaginationListExample() {
var itemsList by remember { mutableStateOf((1..10).toList()) }
val listState = rememberLazyListState()
val loadMore = remember {
derivedStateOf {
val layoutInfo = listState.layoutInfo
val totalItemsNumber = layoutInfo.totalItemsCount
val lastVisibleItemIndex = (layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0) + 1
lastVisibleItemIndex > totalItemsNumber - 2
}
}
LaunchedEffect(loadMore.value) {
if (loadMore.value) {
val newItems = (itemsList.last() + 1..itemsList.last() + 10).toList()
itemsList = itemsList + newItems
}
}
LazyColumn(state = listState) {
items(itemsList) { item ->
Text(text = "Item $item")
}
}
}
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
// 模拟数据类
data class Item(val name: String)
// 分模块加载
@Composable
fun LazyColumnWithModules() {
val moduleBData = listOf(
Item("Item 1"),
Item("Item 2"),
Item("Item 3")
)
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// 模块 A
item {
ModuleA()
}
// 模块 B
item {
ModuleB(moduleBData)
}
// 模块 C
item {
ModuleC()
}
}
}
@Composable
fun ModuleA() {
Box(
modifier = Modifier
.fillMaxWidth()
.background(Color.LightGray)
.padding(16.dp)
) {
Text(text = "Module A")
}
}
@Composable
fun ModuleB(data: List<Item>) {
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(16.dp))
.background(Color.Gray)
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
data.forEach { item ->
Box(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
.border(1.dp, Color.Black, RoundedCornerShape(8.dp))
.padding(16.dp)
) {
Text(text = item.name)
}
}
}
}
@Composable
fun ModuleC() {
Box(
modifier = Modifier
.fillMaxWidth()
.background(Color.LightGray)
.padding(16.dp)
) {
Text(text = "Module C")
}
}
注意
1 避免过度嵌套
在 LazyColumn 中嵌套其他 LazyColumn 或复杂布局可能导致性能问题。
2 状态管理
如果列表项包含状态(如复选框、输入框等),建议将状态提升到外部以避免不必要的重组。
简单看一下LazyColumn源码
1. LazyColumn 函数定义
LazyColumn 本质上是一个 Composable 函数,其定义位于 androidx.compose.foundation.lazy 包下。下面是简化后的函数:
@Composable
fun LazyColumn(
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
content: LazyListScope.() -> Unit
) {
LazyList(
modifier = modifier,
state = state,
contentPadding = contentPadding,
reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement,
horizontalAlignment = horizontalAlignment,
flingBehavior = flingBehavior,
orientation = Orientation.Vertical,
content = content
)
}
分析:
modifier:用于修饰 LazyColumn 的外观和行为,例如设置大小、边距、背景等。
state:LazyListState 类型,用于管理列表的滚动状态,如滚动位置、滚动偏移量等。
contentPadding:PaddingValues 类型,用于设置列表内容的内边距。
reverseLayout:布尔类型,若为 true,列表将从底部开始反向布局。
verticalArrangement:Arrangement.Vertical 类型,用于指定列表项在垂直方向上的排列方式,如顶部对齐、居中对齐等。
horizontalAlignment:Alignment.Horizontal 类型,用于指定列表项在水平方向上的对齐方式,如左对齐、居中对齐等。
flingBehavior:FlingBehavior 类型,用于定义列表在快速滑动时的滚动行为。
content:LazyListScope.() -> Unit 类型,这是一个作用域函数,用于定义列表的内容,即列表项的具体布局
2. LazyList 函数调用
LazyColumn内部调用了LazyList 函数,LazyList是一个更通用的列表组件,支持垂直和水平滚动。以下是 LazyList函数的简化:
@Composable
private fun LazyList(
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
orientation: Orientation,
content: LazyListScope.() -> Unit
) {
// 实现逻辑
}
分析:
orientation:Orientation 类型,指定列表的滚动方向,LazyColumn 中传入的是 Orientation.Vertical。
3. 懒加载机制实现
LazyColumn 的核心特性是懒加载,即只有当列表项进入可视区域时才会进行渲染。这一机制主要通过 LazyListState 和 LazyListLayout 来实现。
3.1 LazyListState
LazyListState 用于管理列表的滚动状态,包括滚动位置、滚动偏移量等。它内部维护了一个 LayoutInfo 对象,用于记录列表项的布局信息。通过 LazyListState,可以实现滚动到指定位置、监听滚动事件等功能。
3.2 LazyListLayout
LazyListLayout 是 LazyColumn 的布局实现类,它继承自 Layout 组件。在 LazyListLayout 中,会根据当前的滚动状态和可视区域,计算需要渲染的列表项,并调用相应的 Composable 函数进行渲染。具体实现逻辑如下:
测量阶段:根据列表项的大小和滚动状态,计算需要渲染的列表项的数量和位置。
布局阶段:将需要渲染的列表项布局到可视区域内,并根据滚动状态进行偏移。
4. 列表项管理
LazyColumn 通过 LazyListScope 来管理列表项。LazyListScope 提供了多个方法,用于添加不同类型的列表项,例如 items、item 等。
4.1 items 方法
items 方法用于添加一组相同类型的列表项,其简化如下:
fun <T> LazyListScope.items(
items: List<T>,
key: ((item: T) -> Any)? = null,
contentType: ((item: T) -> Any)? = null,
itemContent: @Composable LazyItemScope.(item: T) -> Unit
)
分析:
items:列表数据,即需要显示的列表项集合。
key:可选参数,用于为每个列表项提供一个唯一的键,以提高列表的性能和稳定性。
contentType:可选参数,用于指定列表项的类型,以便在渲染时进行优化。
itemContent:Composable 函数,用于定义每个列表项的具体布局。
4.2 item 方法
item 方法用于添加单个列表项,其简化如下:
fun LazyListScope.item(
key: Any? = null,
contentType: Any? = null,
content: @Composable LazyItemScope.() -> Unit
)
分析:
key:可选参数,用于为列表项提供一个唯一的键。
contentType:可选参数,用于指定列表项的类型。
content:Composable 函数,用于定义列表项的具体布局。
简单总结,LazyColumn 的源码主要围绕懒加载机制和列表项管理展开。通过 LazyListState 和 LazyListLayout 实现了懒加载,只有在列表项进入可视区域时才进行渲染,从而提高了性能。同时,通过 LazyListScope 提供的方法,可以方便地管理不同类型的列表项。