从 XML + View 体系切到 Jetpack Compose,最大的敌人不是语法,而是思维惯性。以下是我入门阶段踩过的坑,写给同样在转型的同学。
坑 1:忘记「状态驱动 UI」
XML 时代我们 findViewById 然后手动改控件;Compose 里你要做的是改变状态,UI 自己重组:
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("点击了 $count 次")
}
}
别再想着「拿到 TextView 引用」——Compose 里没有这种操作。
坑 2:remember 与 rememberSaveable 分不清
remember:配置变更(转屏)后会丢;rememberSaveable:能跨配置变更保存(自动存进 Bundle);- 存复杂对象?给 Saver 或转成可保存的类型,别硬塞。
坑 3:状态提升(State Hoisting)没搞懂
子组件需要的数据尽量由父组件持有,通过参数传下来;子组件要改状态就回调上去。这样每个 Composable 都可预览、可测试、可复用。
判断标准:这个状态离开当前界面还需要吗?需要就往上提。
坑 4:LazyColumn 忘加 key
LazyColumn {
items(list, key = { it.id }) { item -> ... }
}
不加 key,列表增删时会出现错位、动画异常。key 要稳定且唯一。
坑 5:在 Composable 里直接做耗时操作
网络请求、数据库读写不能直接写在 Composable 函数体里(重组会重复执行)。正确姿势:
- 协程:
LaunchedEffect/rememberCoroutineScope; - 状态:
StateFlow+collectAsState(); - 架构:ViewModel + Repository,UI 层保持「纯展示」。
坑 6~10 快速清单
- 06:Modifier 顺序影响效果(
padding在clickable前后完全不同); - 07:预览函数写不写
@Preview的showBackground,深色下看不到控件; - 08:
Text里中文要指定字体族,否则部分机型显示异常; - 09:版本不匹配(Compose BOM 与 Kotlin 插件)直接编译报错——先锁 BOM;
- 10:重组不是免费的,大列表记得用
derivedStateOf减少无谓重组。
最后
Compose 上手曲线比 XML 陡,但一旦想通「状态 → UI」的模型,写界面会变成一件很爽的事。坑是踩不完的,关键是每次踩完都记下来——就像这篇笔记一样。