跳到正文Lab学后总结 · Jimmy Huang's BlogLab学后总结
本页目录· 87 条
1. 一句话总结这个 Lab
这个 Lab 要教会你:在 Jetpack Compose 里用 LazyColumn 显示一个可滚动列表,并通过 State 让用户的点击、删除、输入、新增等操作自动反映到界面上。
换成更直白的话就是:
你不是在单纯做“国家列表”,而是在练习 Compose 最核心的思想:数据变了,UI 自动跟着变。
2. 这个 Lab 实际做了什么
根据页面可见内容,这个 Lab 大致是按“从静态列表到可交互列表”的顺序推进的。
2.1 创建 Android 项目入口
你先创建了一个新的 Android Studio 项目,页面可见名称是:
这个项目就是本周实验的练习场。你会在里面写多个 Composable 函数,然后通过修改 setContent { ... } 里调用的函数,切换当前要运行和截图的版本。
例如概念上可以理解为:
注意:具体函数名和完整代码材料未完整显示,上面只是帮助你理解 setContent 的作用,不代表原 Lab 一定使用这些名字。
2.2 显示一个基础列表
一开始,你先把一个国家列表显示出来。
这里的重点不是“国家”本身,而是学会:
可以理解成:
LazyColumn 是 Compose 里的“可滚动列表容器”,适合显示很多条数据。
2.3 点击某一项,并显示当前选中的结果
点击某个国家后,界面上会显示“当前选中了哪个国家”。
也就是说,点击本身不是最终目的,点击之后更新状态,状态再驱动界面变化才是重点。
2.4 删除列表项
remember
mutableStateOf
mutableStateListOf
所以本 Lab 的重点之一就是理解:普通变量变化不一定会让 Compose 更新界面;被 Compose 观察到的 state 变化,才会触发界面更新。
2.5 输入文字并新增列表项
接着,你用 TextField 做一个输入框,让用户输入新的国家名称,再点击按钮添加到列表里。
用户输入 Japan
输入框 state 变成 Japan
用户点 Add
程序把 Japan 加进列表 state
列表 state 变了
LazyColumn 自动多显示一行
通常还会把输入框清空
TextField 显示什么,也要由 state 控制;列表显示什么,也要由 state 控制。
2.6 让键盘 Done / Enter 也能新增
后半段把新增体验升级了:不仅可以点按钮新增,还可以按键盘上的 Done 或 Enter 新增。
比如用户输入完一个项目后,直接按键盘 Done 就提交,比必须收起键盘再点按钮更顺手。
2.7 补充练习 ExposedDropdownMenu
最后还补了一个 ExposedDropdownMenu 练习。
这部分不是列表的主流程,但它和前面的 TextField、state 思想是一致的:
当前选中的选项是 state
下拉菜单展开/收起也是 state
用户选择某个选项后,state 改变
UI 显示新的选择结果
3. 每个核心组件有什么用
3.1 Composable
它是什么
Composable 是 Jetpack Compose 里用来描述 UI 的函数。
一个页面可以是一个 Composable
一个列表项可以是一个 Composable
一个按钮区域也可以是一个 Composable
它在这个 Lab 里负责什么
这个 Lab 里,你会写多个版本的 Composable:
最基础的列表版本
可点击选中的列表版本
可删除的列表版本
可输入新增的列表版本
带下拉菜单的练习版本
页面可见内容建议:把所有版本的 Composable 都保留在同一个文件里,每次只切换 setContent { ... } 里当前调用的函数名。
不容易覆盖前面的版本
方便对比学习过程
方便按阶段截图
出错时可以退回上一个可运行版本
没有它会发生什么
没有 Composable,你就没有地方用 Compose 的方式写 UI。
在传统 Android 里,你可能用 XML 写界面;但在 Compose 里,UI 是用 Kotlin 函数描述出来的。
和其他组件有什么关系
Composable 读取 state,然后根据 state 画 UI。
3.2 setContent { ... }
它是什么
setContent { ... } 是 Android Activity 里设置 Compose UI 的入口。
App 启动后,告诉屏幕:这次我要显示哪个 Compose 界面。
它在这个 Lab 里负责什么
页面可见内容建议每次只切换 setContent { ... } 里的当前函数名。
例如你今天要看“可删除列表”版本,就让它调用可删除版本的 Composable。
没有它会发生什么
如果没有正确设置 setContent,你的 Composable 函数可能写了但不会显示出来。
这就像你做了一张海报,但没有贴到公告栏上,别人看不到。
和其他组件有什么关系
setContent 是入口,Composable 是具体界面,State 是界面背后的数据。
3.3 LazyColumn
它是什么
LazyColumn 是 Jetpack Compose 里用来显示垂直滚动列表的组件。
它在这个 Lab 里负责什么
没有它会发生什么
如果不用 LazyColumn,你可能会用普通 Column。
普通 Column 适合少量固定内容,但如果列表很多,可能会出现:
不能自然滚动
一次性加载太多内容
性能不好
代码不适合真实项目
和其他组件有什么关系
LazyColumn 不负责保存数据,它只是负责“显示数据”。
3.4 remember
它是什么
remember 用来让 Compose 在重组时记住某个值。
它在这个 Lab 里负责什么
当前选中的国家
输入框里的文字
当前国家列表
下拉菜单是否展开
下拉菜单当前选中的选项
没有它会发生什么
如果不用 remember,界面每次重新组合时,某些变量可能会被重新初始化。
比如你刚输入 Japan,结果界面刷新后输入框又变回空字符串。
和其他组件有什么关系
remember 常常和 mutableStateOf 搭配使用:
3.5 mutableStateOf
它是什么
mutableStateOf 用来创建一个 Compose 可以观察的可变状态。
这是一个带通知功能的变量。它变了,Compose 会知道。
它在这个 Lab 里负责什么
当前输入框文字
当前选中的国家
下拉菜单是否展开
当前选中的下拉选项
没有它会发生什么
当这个变量变化时,Compose 不一定会知道它变了,因此界面不一定更新。
和其他组件有什么关系
mutableStateOf 是 Compose UI 更新的核心。
3.6 mutableStateListOf
它是什么
mutableStateListOf 是 Compose 里的状态列表。
它适合保存会被增删改的列表,例如本 Lab 的国家列表。
它在这个 Lab 里负责什么
没有它会发生什么
你即使把某一项删掉,Compose 也不一定知道列表已经变化。
数据变了,但 UI 没刷新
你以为删除失败
实际上只是界面没有跟着更新
和其他组件有什么关系
mutableStateListOf 是数据源,LazyColumn 是展示层。
3.7 TextField
它是什么
TextField 是 Compose 里的输入框。
它在这个 Lab 里负责什么
它让用户输入新的国家名称,然后配合按钮或键盘动作,把输入内容添加进列表。
没有它会发生什么
如果没有 TextField,用户只能看列表、点列表、删列表,不能主动输入新内容。
这个 Lab 就无法从“可操作列表”升级到“可编辑列表”。
和其他组件有什么关系
3.8 Button / Add 按钮
它是什么
它在这个 Lab 里负责什么
没有它会发生什么
虽然可以只靠键盘 Done/Enter 提交,但对初学者和普通用户来说,有一个 Add 按钮会更清楚。
和其他组件有什么关系
Button 通常会读取 TextField 的输入 state,然后修改列表 state。
3.9 Keyboard Done / Enter
它是什么
在手机软键盘上常见的是 Done;在硬件键盘上可能是 Enter。
它在这个 Lab 里负责什么
它让用户输入完后可以直接提交,不一定非要点 Add 按钮。
没有它会发生什么
和其他组件有什么关系
它和 Button 做的事情类似:都是触发“把输入内容加入列表”。
所以比较好的写法是把新增逻辑抽成同一个函数,让按钮和键盘动作都调用它。
3.10 ExposedDropdownMenu
它是什么
ExposedDropdownMenu 是 Material 风格的下拉选择菜单。
它常常和输入框或只读输入框一起使用,让用户从几个选项里选一个。
它在这个 Lab 里负责什么
页面可见内容说明最后补了一个 ExposedDropdownMenu 练习,用来理解 Compose 里下拉选择组件怎么做。
没有它会发生什么
这个 Lab 的列表主线仍然能完成,但你会少练一个真实 App 中很常见的表单控件。
比如以后做 FreshGuard 这种 App 时,可能会用下拉菜单选择:
和其他组件有什么关系
是否展开:一个 Boolean state
当前选中项:一个选项 state
用户点击菜单项后,更新选中项 state,UI 再显示新的选择结果。
4. 用初学者类比解释
4.1 整个 App 像一个小餐厅
看菜单
点某一道菜
删除一道菜
新增一道菜
从下拉菜单里选分类
4.2 State 像前台手里的真实订单本
4.3 LazyColumn 像电子菜单显示屏
它只是一个显示屏,负责把订单本上的内容一行一行展示出来。
如果订单本里有 100 道菜,显示屏不需要一次性把 100 道菜全部高成本画出来,而是优先显示当前屏幕附近的内容。
4.4 TextField 像前台输入框
4.5 Add 按钮像“提交订单”按钮
用户输入完 New Zealand 后,点击 Add。
4.6 Done / Enter 像快捷提交键
有些顾客不想再点按钮,输入完直接按键盘上的 Done。
所以 Done/Enter 和 Add 按钮本质上做的是同一件事。
4.7 ExposedDropdownMenu 像前台的分类选择器
5. 重点区分容易混淆的概念
5.1 Column 和 LazyColumn 的区别
5.2 remember 和 mutableStateOf 的区别
5.3 普通变量和 Compose State 的区别
你应该使用 Compose 能观察的 state。
5.4 mutableStateOf(list) 和 mutableStateListOf 的区别
这个 Lab 涉及删除和新增列表项,所以 mutableStateListOf 特别适合拿来解释。
5.5 TextField 的 state 和列表的 state 不是同一个
5.6 点击列表项和删除列表项不是一回事
它们都可能发生在同一行 UI 上,但修改的 state 不一样。
5.7 Add 按钮和 Done / Enter 的区别
点 Add 会清空输入框
按 Done 不会清空输入框
点 Add 会检查空字符串
按 Done 忘记检查空字符串
5.8 LazyColumn 不是数据库
如果你关闭 App 再打开,数据是否还在,取决于你有没有使用:
ViewModel
Room
DataStore
文件存储
后端数据库
本 Lab 可见内容主要是前两个,不是数据库 Lab。
5.9 这个 Lab 不涉及网络控制、权限控制、后端 API
谁能访问谁
哪个是入口
哪个是数据库
哪个是权限控制
哪个是网络控制
但这个 Week 4 Compose Lab 是本地 UI Lab。
6. 这个 Lab 有什么实际用途
这个 Lab 虽然看起来只是一个“国家列表”小练习,但它对应真实 Android 项目里非常常见的能力。
6.1 做任何列表页都需要它
商品列表
聊天消息列表
食物捐赠列表
订单列表
通知列表
搜索结果列表
评论列表
设置项列表
6.2 做可交互 UI 必须理解 State
6.3 对 FreshGuard 这类作业很有用
如果你之后做 FreshGuard 这类 Android App,这个 Lab 可以直接迁移到很多功能里:
所以这个 Lab 不是孤立知识,它是后续 App 开发的基础。
6.4 对后端开发也有帮助
即使你以后主要做后端,也需要理解前端如何展示数据。
前端拿到后,可能就用 LazyColumn 显示。
6.5 对 DevOps / 云架构理解也有帮助
虽然这个 Lab 不涉及云部署,但它帮助你理解应用内部的数据流。
这个 Week 4 Lab 学的是其中前端本地部分:
7. 最后画一张生动形象的示意图
下面这张图不是从原材料截图复制的,而是基于页面可见内容整理出的教学版流程图。它用来帮助你理解本 Lab 的核心结构。
如果 Mermaid 没有渲染,可以看下面的文字版:
7.1 这张图最重要的理解方式
最后复习:你应该带走的 5 个重点
LazyColumn 是显示列表的,不是保存数据的。
State 是 Compose UI 的数据来源。
remember 负责让 state 在重组时不要丢。
mutableStateOf 适合单个值,mutableStateListOf 适合可增删的列表。
点击、删除、输入、Done/Enter、下拉选择,本质都是用户事件;事件修改 state,state 驱动 UI 更新。
setContent {
// 当前想看哪个版本,就调用哪个 Composable
// CountryListDemo()
// SelectableCountryList()
// EditableCountryList()
}
State 当前是什么 → Composable 就画出什么
Activity → setContent → Composable → 读取 State → 显示 UI
列表 State → LazyColumn 读取 → 屏幕显示列表行
用户操作 → 修改 mutableStateOf → Compose 重组 → UI 更新
mutableStateListOf 保存列表数据
LazyColumn 根据列表数据画出一行行 UI
用户输入文字 → TextField 的 text state 改变 → 用户点击 Add / 按 Done → 加入列表 state → LazyColumn 更新
Button onClick → 读取输入框 state → 修改列表 state
Add 按钮 ┐
├→ addCountry()
Done 键 ┘
Australia
Japan
New Zealand
TextField 当前文字 = 一个 state
输入框 state = Japan
列表 state = Australia, China
输入框 state = 空
列表 state = Australia, China, Japan
Add 按钮 和 Done/Enter → 都调用同一个 add 函数
LazyColumn = 显示列表
State = 当前内存里的数据
Database = 持久保存数据
数据列表 → LazyColumn → 一行一行显示
修改 state → Compose 自动重组 → UI 更新
后端返回数据 → 前端保存为 state → LazyColumn 显示
用户操作 UI
↓
前端 state 改变
↓
必要时调用 API
↓
后端处理
↓
数据库保存
↓
返回新数据
↓
前端列表更新
用户操作 UI → state 改变 → UI 更新
flowchart TB
title["Lab Week 4:Compose LazyColumn 交互列表学习图"]
user["用户\n点击 / 输入 / 删除 / 按 Done"]
app["Android App\nLazyColumnLab"]
entry["setContent { ... }\nCompose UI 入口"]
stateBox["State 状态区\nremember + mutableStateOf\nmutableStateListOf"]
inputState["输入框 State\n当前正在输入的文字"]
selectedState["选中项 State\n当前选中的国家"]
listState["列表 State\n当前国家列表"]
menuState["下拉菜单 State\n是否展开 + 当前选项"]
uiBox["Composable UI 显示区"]
textField["TextField\n输入新国家"]
addButton["Add 按钮\n提交输入"]
keyboard["Keyboard Done / Enter\n快捷提交"]
lazyColumn["LazyColumn\n显示可滚动国家列表"]
row["列表项 Row\n点击选中 / 删除"]
dropdown["ExposedDropdownMenu\n下拉选择"]
blocked1["❌ 不能绕过 State\n直接长期修改 UI"]
blocked2["❌ 普通 MutableList 变化\n不保证触发 UI 更新"]
summary["你要记住的核心区别:\n1. LazyColumn 只负责显示列表,不是数据库。\n2. State 才是 UI 当前内容的来源。\n3. 用户操作不是直接改屏幕,而是先改 State。\n4. State 变了,Compose 才会自动重组并刷新 UI。\n5. Add 按钮和 Done/Enter 本质都是提交输入。"]
title --> app
user --> app
app --> entry
entry --> uiBox
uiBox --> textField
uiBox --> addButton
uiBox --> keyboard
uiBox --> lazyColumn
uiBox --> dropdown
lazyColumn --> row
textField --> inputState
addButton --> listState
keyboard --> listState
row --> selectedState
row --> listState
dropdown --> menuState
inputState --> stateBox
selectedState --> stateBox
listState --> stateBox
menuState --> stateBox
stateBox --> uiBox
user -. "想直接改屏幕" .-> blocked1
blocked1 -. "被阻止:Compose 应该由 State 驱动" .-> stateBox
user -. "只改普通列表变量" .-> blocked2
blocked2 -. "被阻止:UI 可能不知道数据变了" .-> listState
stateBox --> summary
classDef important fill:#E8F5E9,stroke:#2E7D32,stroke-width:2px,color:#111;
classDef ui fill:#E3F2FD,stroke:#1565C0,stroke-width:2px,color:#111;
classDef state fill:#FFF8E1,stroke:#F9A825,stroke-width:2px,color:#111;
classDef bad fill:#FFEBEE,stroke:#C62828,stroke-width:3px,color:#111;
classDef summary fill:#F3E5F5,stroke:#6A1B9A,stroke-width:2px,color:#111;
class app,entry important;
class uiBox,textField,addButton,keyboard,lazyColumn,row,dropdown ui;
class stateBox,inputState,selectedState,listState,menuState state;
class blocked1,blocked2 bad;
class summary summary;
用户操作
├─ 输入文字 → TextField → 输入框 State
├─ 点击 Add → 把输入内容加入列表 State
├─ 按 Done/Enter → 把输入内容加入列表 State
├─ 点击列表项 → 更新选中项 State
├─ 点击删除 → 从列表 State 删除项目
└─ 选择下拉菜单 → 更新下拉菜单 State
State 状态区
├─ 输入框文字
├─ 当前选中项
├─ 当前国家列表
└─ 下拉菜单状态
↓
Composable UI 根据 State 重新绘制
↓
LazyColumn 显示最新列表
用户点一下 → 我修改 State → Compose 自动根据新 State 画屏幕
var text by remember { mutableStateOf("") }
var selectedCountry = "Australia"
val countries = mutableListOf("Australia", "China", "Japan")
var text by remember { mutableStateOf("") }
var selected = "Australia"
[
{ "name": "Apple" },
{ "name": "Banana" }
]