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

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

相关推荐
好运的阿财2 小时前
“锟斤拷”问题——程序中用powershell执行命令出现中文乱码的解决办法
linux·前端·人工智能·机器学习·架构·编辑器·vim
踩着两条虫2 小时前
VTJ.PRO AI + 低代码实战:接入高德地图
前端·vue.js·ai编程
绝世唐门三哥2 小时前
React性能优化:memo、useMemo和useCallback全解析
前端·react.js·memo
兔子零10242 小时前
Claude Code 都把宠物养进终端了,我做了一个真正能长期玩的中文宠物游戏
前端·游戏·游戏开发
xiaotao1312 小时前
Vite 与 Webpack 开发/打包时环境变量对比
前端·vue.js·webpack
摆烂工程师2 小时前
教你如何查询 Codex 最新额度是多少,以及 ChatGPT Pro、Plus、Business 最新额度变化
前端·后端·ai编程
捧月华如2 小时前
响应式设计原理与实践:适配多端设备的前端秘籍
前端·前端框架·json
笨笨狗吞噬者2 小时前
VSCode 插件推荐 Copy Filename Pro,快速复制文件、目录和路径的首选
前端·visual studio code
Armouy2 小时前
Electron:核心概念、性能优化与兼容问题
前端·javascript·electron