Compose 组件:LazyColumn 核心参数与 key/contentType 详解

本文内容结合扔物线视频课程、与个人理解、ai案例。

1. 关键入参

  1. contentPadding:使得 item 可覆盖间距。
  1. verticalArrangement:通常用 spacedBy,设置子项间间距
  1. reverseLayout:倒着排版
  1. LazyListState:控制与监听列表
  • 跳转;控制点击 item 时滑动到对应 item。
kotlin 复制代码
@Preview
@Composable
private fun LazyRowTest() {
    val listState = rememberLazyListState()
    val scope = rememberCoroutineScope()
    LazyColumn(
        ...,
    state = listState
    ) {
        items(30) { index ->
            ListItem(index) {
                scope.launch {
                    listState.scrollToItem(index)
                }
            }
        }
    }
}
  • 监听第一个可见 item;

除了 firstVisibleItemIndex,还有 firstVisibleItemScrollOffset/layoutInfo 可以获取更多信息。

  1. userScrollEnabled

控制列表是否可以滑动

  1. flingBehavior

控制 fling 惯性滑动的物理引擎,默认情况是慢慢减速移动。例如可以控制 fling 速度、停止时将第一个可见的 item 吸附顶部。

  1. overscrollEffect

滑动过头继续滑动的效果。

2. key(减少重建次数)

首先,构造一个实验场景:

kotlin 复制代码
val users = remember {
    listOf(
        User(1, "张三"),
        User(2, "李四"),
        User(3, "王五"),
        User(4, "赵六"),
        User(5, "钱七"),
    ).toMutableStateList()
}

// 有 key 
LazyColumn(...) {
    items(users.size, key = { users[it].id } ) { index ->
        UserItem(users[index])
    }
}

// 无 key,默认 key 为 index
LazyColumn(...) {
    items(users.size) { index ->
        UserItem(users[index])
    }
}

点击「改名」:"张三" 改名为 "张三✓" -> users[0]发生改变 -> 触发两个列表重组。

这是 StateList 快照 的作用。

这时头部插入一个 item,没有 key 的列表项都发生了一次重组。

而有 key 的列表,没有发生新的重组。

这个过程中发生了什么:

  1. 列表数据变了,LazyColumn 重新遍历所有 item,调用 key lambda 生成新的 key 列表:

旧 key 列表:[1, 2, 3, 4, 5]

新 key 列表:[105, 1, 2, 3, 4, 5]

  1. 逐个匹配新 key → 旧 key

对新 key 列表中的每一个 key,去旧 key 列表里找:

新 key=105 → 旧列表里找不到 → 标记为「新建」

新 key=1 → 旧列表里找到了 → 标记为「复用」

新 key=2 → 旧列表里找到了 → 标记为「复用」

新 key=3 → 旧列表里找到了 → 标记为「复用」

新 key=4 → 旧列表里找到了 → 标记为「复用」

新 key=5 → 旧列表里找到了 → 标记为「复用」

  1. 对每个「复用」的 item,执行 composable lambda

这一步才是决定重不重组的关键。以 key=1(张三)为例:

scss 复制代码
items(users.size, key = { users[it].id }) { index ->                                  
      UserItem(users[index])  // ← 执行这个 lambda          
}

Compose 执行 lambda,里面会读取 users[index]。这时 Compose 的 State 快照系统介入:

key=1 的 item,现在 index 从 0 变成了 1

读取 users[1] → 得到 User(1, "张三✓")

上次这个 item 读到的也是 User(1, "张三✓")

→ 数据没变 → 重组

而对于没有 key 的场景:

旧 key 列表:[0, 1, 2, 3, 4]

新 key 列表:[0, 1, 2, 3, 4, 5]

新 key=0 → 旧列表找到了 → 复用 → 但 users[0] 从"张三✓"变成"新人5" → 重组

新 key=1 → 旧列表找到了 → 复用 → 但 users[1] 从"李四"变成"张三✓" → 重组

新 key=2 → 旧列表找到了 → 复用 → 但 users[2] 从"王五"变成"李四" → 重组

...

新 key=5 → 旧列表找不到 → 新建

3. contentType(减少重建次数)

告诉 LazyColumn:不同 item 的布局类型是什么,以便更高效地复用组合节点。

假设列表有两种布局:文字消息和图片消息,交替排列。

diff 复制代码
index 0 → TextBubble   (一个 Text)
index 1 → ImageBubble  (一个 Image + 一个 Text)
index 2 → TextBubble
index 3 → ImageBubble
...
  1. 不填 contentType:

向下滚动,index 0 滑出,index 4 (TextBubble) 要滑入:

运气好:从池里拿到 TextBubble 节点 → 结构匹配 → 只更新数据 → 快

继续滚动,index 1 滑出,index 5 (ImageBubble) 要滑入:

运气差:池里先进先出,拿到的是 TextBubble 节点 → 结构不匹配 → 整个 composition 要拆掉重建 → 慢

  1. 填 contentType

回收池按类型分开,永远不会拿错类型。

相关推荐
山楂树の9 小时前
图像标注大坑:img图片 + Canvas 叠加标注,同步放大后标注位置偏移、对不齐?详解修复方案及亚像素处理原理
前端·css·学习·canva可画
本山德彪9 小时前
我做了一个拼豆图纸生成器,把照片秒变图纸
前端
DTrader10 小时前
用TS无法实盘量化? - 实盘均线策略
前端·api
进击的夸父10 小时前
vfojs:Vue 超集架构,外壳React灵魂Vue
前端
编程老船长10 小时前
解决不同项目需要不同 Node.js 版本的问题
前端·vue.js
Wect10 小时前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·算法·typescript
漫游的渔夫10 小时前
前端开发者做 Agent:别写成一次请求,用 5 步受控循环防止 AI 乱跑
前端·人工智能·typescript
kyriewen11 小时前
Webpack vs Vite:一个是“老黄牛”,一个是“猎豹”,你选谁?
前端·webpack·vite
打小就很皮...11 小时前
html2canvas + jsPDF 生成 PDF 的踩坑与解决方案总结
前端·pdf
全栈前端老曹11 小时前
【前端地图】多地图平台适配方案——高德、百度、腾讯、Google Maps SDK 差异对比、封装统一地图接口
前端·javascript·百度·dubbo·wgs84·gcj-02·bd09