列表与滚动
大约 5 分钟
列表与滚动
本页说明 Kuikly Compose 中列表与滚动组件的支持情况与使用注意事项。
基础用法和官方保持一致,请优先查阅 Jetpack Compose 官方文档。
官方文档(推荐阅读):Lists and grids
常用组件
列表
LazyColumn/LazyRow:懒加载的纵向 / 横向列表
网格与瀑布流
LazyVerticalGrid/LazyHorizontalGrid:网格LazyVerticalStaggeredGrid/LazyHorizontalStaggeredGrid:瀑布流
分页
HorizontalPager/VerticalPager:分页容器
特别说明:以上组件本身自带滚动能力
扩展能力
回弹控制:Modifier.bouncesEnable
@Composable
fun NoBounceList(data: List<String>) {
LazyColumn(
modifier = Modifier.bouncesEnable(false) // 关闭回弹
) {
items(data) { item ->
Text(text = item)
}
}
}
嵌套滚动策略:Modifier.nestedScroll
- 扩展函数:
Modifier.nestedScroll(scrollUp: NestedScrollMode, scrollDown: NestedScrollMode) - 枚举
NestedScrollMode:SELF_ONLY/SELF_FIRST/PARENT_FIRST - 作用:精细控制子列表与父容器(如外层 Scroller / Pager)在上下滚动时的优先级与联动策略。
- 示例:
- 上滑父优先、下拉子优先:
@Composable
fun NestedScrollList(data: List<String>) {
LazyColumn(
modifier = Modifier.nestedScroll(
scrollUp = NestedScrollMode.SELF_FIRST,
scrollDown = NestedScrollMode.PARENT_FIRST
)
) {
items(data) { item ->
Text(text = item)
}
}
}
预加载配置:beyondViewportPageCount / beyondBoundsItemCount
- HorizontalPager / VerticalPager
- 参数:
beyondViewportPageCount: Int - 含义:在当前可见页面之前 / 之后额外预加载的页面数量,用于减少快速滑动时的白屏和抖动。
- 建议:一般设置为 1~3,数值过大会增加一次性组合和布局的页数,影响首帧和内存。
- 参数:
- LazyColumn / LazyRow
- 当前不提供显式的
beyondXXX参数,但内部同样会根据滚动方向做预取和「视窗之外」的 item 组合; - 你可以通过控制
item渲染开销、contentPadding、item高度/宽度等方式,间接影响预加载体验。
- 当前不提供显式的
- LazyVerticalStaggeredGrid / LazyHorizontalStaggeredGrid
- 参数:
beyondBoundsItemCount: Int - 含义:在当前可见区域之外额外预加载的 item 数量,用于瀑布流滚动时提前测量和布局后续单元格。
- 建议:根据单个 item 渲染开销和屏幕尺寸来调节,通常 4~10 之间即可。
- 参数:
@Composable
fun PreloadPagerSample() {
val pagerState = rememberPagerState(pageCount = { 10 })
HorizontalPager(
state = pagerState,
beyondViewportPageCount = 2, // 左右各预加载 2 页
modifier = Modifier
.height(200.dp)
.fillMaxWidth()
) { page ->
Box(
modifier = Modifier
.fillMaxSize()
.background(if (page % 2 == 0) Color(0xFFE3F2FD) else Color(0xFFFFF3E0)),
contentAlignment = Alignment.Center
) {
Text("Page $page")
}
}
}
@Composable
fun PreloadStaggeredGridSample(items: List<String>) {
LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Fixed(2),
beyondBoundsItemCount = 8, // 预加载可见区域外的 8 个 item
modifier = Modifier
.fillMaxSize()
.background(Color.LightGray),
contentPadding = PaddingValues(8.dp)
) {
items(items.size, key = { index -> items[index] }) { index ->
Text(
text = items[index],
modifier = Modifier
.padding(4.dp)
.fillMaxWidth()
.background(Color.White)
.padding(8.dp)
)
}
}
}
下拉刷新:pullToRefreshItem
@Composable
fun PullToRefreshList(data: List<String>) {
val lazyListState = rememberLazyListState()
var isRefreshing by remember { mutableStateOf(false) }
var items by remember { mutableStateOf(data) }
val pullToRefreshState = rememberPullToRefreshState(isRefreshing)
LazyColumn(
state = lazyListState,
modifier = Modifier
.fillMaxSize()
.background(Color.White),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(8.dp)
) {
// 1. 下拉刷新头部,必须放在列表最前面
pullToRefreshItem(
state = pullToRefreshState,
scrollState = lazyListState,
onRefresh = {
// 这里可以触发实际的网络请求
isRefreshing = true
items = listOf("刷新后的 Item 1", "刷新后的 Item 2") + data
isRefreshing = false
}
)
// 2. 正常业务列表内容
items(items) { item ->
Box(
modifier = Modifier
.fillMaxWidth()
.background(Color(0xFFF5F5F5))
.padding(12.dp)
) {
Text(text = item)
}
}
}
}
示例:基础纵向列表(LazyColumn)
@Composable
fun SimpleColumnList(
data: List<String>,
onItemClick: (String) -> Unit = {}
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF5F5F5)),
contentPadding = PaddingValues(vertical = 8.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
itemsIndexed(
items = data,
key = { _, item -> item }
) { index, item ->
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 4.dp)
.background(Color.White)
.clickable { onItemClick(item) }
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "${index + 1}",
style = MaterialTheme.typography.labelMedium,
color = Color(0xFF9E9E9E),
)
Spacer(Modifier.width(12.dp))
Column {
Text(
text = item,
style = MaterialTheme.typography.bodyLarge
)
Text(
text = "这是一条描述文案,用于展示同一个 item 内多行内容的情况。",
style = MaterialTheme.typography.bodySmall,
color = Color(0xFF9E9E9E),
maxLines = 2
)
}
}
}
}
}
示例:使用 key 保持滚动位置
在长列表中,推荐为每个 item 提供稳定的 key,这样在插入 / 删除 / 重新排序数据时,可以更好地复用已组合的 item,并保持当前滚动位置:
data class Message(val id: Long, val author: String, val content: String)
@Composable
fun MessageList(messages: List<Message>) {
LazyColumn {
items(
items = messages,
key = { message -> message.id } // 使用稳定 id 作为 key
) { message ->
Column(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
) {
Text(text = message.author, style = MaterialTheme.typography.titleMedium)
Spacer(Modifier.height(4.dp))
Text(text = message.content, style = MaterialTheme.typography.bodyMedium)
}
}
}
}
示例:网格与错位网格
@Composable
fun SimpleGrid(items: List<String>) {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF5F5F5)),
contentPadding = PaddingValues(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(
count = items.size,
key = { index -> items[index] }
) { index ->
Column(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
.padding(12.dp)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(80.dp)
.background(if (index % 2 == 0) Color(0xFFE3F2FD) else Color(0xFFFFF3E0))
)
Spacer(Modifier.height(8.dp))
Text(
text = items[index],
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
@Composable
fun SimpleStaggeredGrid(items: List<String>) {
LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Fixed(2),
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF5F5F5)),
contentPadding = PaddingValues(8.dp),
verticalItemSpacing = 8.dp,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(
count = items.size,
key = { index -> items[index] }
) { index ->
val height = if (index % 3 == 0) 120.dp else if (index % 3 == 1) 80.dp else 160.dp
Column(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
.padding(12.dp)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(height)
.background(Color(0xFFE1F5FE))
)
Spacer(Modifier.height(8.dp))
Text(
text = items[index],
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
更多代码示例
以下 Demo 展示了列表与滚动能力的典型用法,可在开源仓库中查看完整代码:
LazyColumnDemo3.kt:LazyColumn各种状态信息示例LazyColumnDemo4.kt:LazyColumn滑动 API 示例LazyRowDemo1.kt:LazyRow基本用法示例LazyRowDemo2.kt:LazyRow的 key / type 参数示例LazyRowDemo3.kt:LazyRow各种状态信息示例LazyHorizontalGridDemo1.kt:LazyHorizontalGrid基本用法示例LazyHorizontalGridDemo2.kt:LazyHorizontalGrid的 key、span 跨行参数示例LazyHorizontalGridDemo3.kt:LazyHorizontalGrid各种状态信息示例StaggeredHorizontalGridDemo1.kt:错位网格布局基础用法示例StaggeredHorizontalGridDemo3.kt:错位网格布局各种状态信息示例HorizontalPagerDemo1.kt:HorizontalPager基本用法示例HorizontalPagerDemo3.kt:HorizontalPager各种状态信息示例LazyColumnNestDemo.kt:LazyColumn嵌套滚动场景示例LazyRowInHorizontalPager.kt:LazyRow嵌套在HorizontalPager中的滚动示例LazyColumnStickyHeader.kt:带粘性头部的LazyColumn示例PullToRefreshDemo.kt:LazyColumn下拉刷新示例
注意事项
- 性能:尽量使用
items/itemsIndexed,避免在 item 中持久创建大对象;长列表建议使用稳定 key。 - 预加载:
beyondViewportPageCount/beyondBoundsItemCount不宜设置过大,一般控制在小范围内(例如 1~3 页、4~10 个 item),否则会明显增加首帧时间和内存占用。 - 嵌套滚动:
LazyColumn/LazyRow与Pager、外层滚动容器嵌套时,优先使用Modifier.nestedScroll、Modifier.bouncesEnable等官方/Kuikly 提供的能力,不建议自行拦截手势事件。 - 状态管理:业务状态尽量 hoist 到列表外(ViewModel / 上层 Composable),避免在
items内部直接remember { mutableStateOf(...) }保存关键状态,以免 item 复用、插入/删除时出现错乱。