从 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 顺序影响效果(paddingclickable 前后完全不同);
  • 07:预览函数写不写 @PreviewshowBackground,深色下看不到控件;
  • 08Text 里中文要指定字体族,否则部分机型显示异常;
  • 09:版本不匹配(Compose BOM 与 Kotlin 插件)直接编译报错——先锁 BOM;
  • 10:重组不是免费的,大列表记得用 derivedStateOf 减少无谓重组。

最后

Compose 上手曲线比 XML 陡,但一旦想通「状态 → UI」的模型,写界面会变成一件很爽的事。坑是踩不完的,关键是每次踩完都记下来——就像这篇笔记一样。