Compose 核心概念
Compose 核心概念
面试高频指数:极高 Compose 是 Android 现代 UI 开发的方向,理解声明式思维是第一步。
1. 声明式 UI vs 命令式 UI
命令式(View 体系):
findViewById → setText → setVisibility → ...(手动操作每一步)
声明式(Compose):
UI = f(state)
状态变化 → 自动重组 UI(无需手动操作)两者的对比在代码里更直观——命令式需要你拿着控件引用一步步改,声明式只需描述"UI 长什么样",状态变了框架自动重绘:
// 命令式(View 体系)
TextView tv = findViewById(R.id.tv_name);
btn.setOnClickListener(v -> {
tv.setText(input.getText()); // 手动更新
tv.setVisibility(View.VISIBLE);
});
// 声明式(Compose)仅支持 Kotlin DSL,无 Java 等价写法;
// 状态驱动自动更新:UI = f(state)// 命令式
val tv = findViewById<TextView>(R.id.tv_name)
btn.setOnClickListener {
tv.text = input.text // 手动更新
tv.visibility = View.VISIBLE
}
// 声明式
@Composable
fun Greeting(name: String) {
if (name.isNotEmpty()) {
Text(text = "Hello, $name") // 状态驱动,自动更新
}
}2. 可组合函数(Composable)
可组合函数是 Compose 的最小构建单元:一个普通函数加上 @Composable 注解,就具备了"参与 UI 组合"的能力。它描述 UI 而不返回视图对象,通过参数接收数据、通过组合自由复用:
@Composable
fun ProfileCard(user: User) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = user.name, style = MaterialTheme.typography.headlineSmall)
Text(text = user.email, style = MaterialTheme.typography.bodyMedium)
Button(onClick = { /* ... */ }) {
Text("Follow")
}
}
}规则:
- 函数用
@Composable注解标记。 - 函数名首字母大写(惯例)。
- 不返回值(描述 UI)。
- 可组合:通过参数与状态组合复用。
- 无副作用:不要在重组中做非 UI 操作(网络、写文件)。
3. 重组(Recomposition)
重组是 Compose 性能的核心机制:状态变化时,Compose 不会重绘整个界面,而是重新执行受影响的函数并智能跳过参数未变的部分。理解重组的前提是理解它的三个关键机制:
状态改变 → 调用方重组 → 只更新受影响的可组合函数
关键机制:
① 智能跳过:参数未变的部分不重组
② 作用域:状态读取被拆分到最小作用域
③ 幂等:重组可被随时取消/重放一个最经典的计数器例子——remember 保住状态,读取状态的地方自动订阅变化:
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) } // 状态
Column {
Text("Count: $count") // 读取状态 → 变化时重组
Button(onClick = { count++ }) {
Text("Add")
}
}
}性能注意:
① 避免在重组中做耗时计算 → 用 remember / derivedStateOf
② 列表用 LazyColumn(懒加载),不要用 Column 包 for 循环
③ 大对象状态提升(hoist),减少重组范围4. Modifier:链式修饰
Modifier 是 Compose 中"修饰 UI 元素"的统一入口:尺寸、间距、背景、点击、语义都可以通过链式调用叠加。注意顺序敏感——clip 在 background 之前,背景才会被圆角裁剪:
// Modifier 按顺序应用(从上到下)
Modifier
.padding(16.dp) // 外边距
.size(100.dp) // 尺寸
.clip(RoundedCornerShape(8.dp)) // 圆角裁剪
.background(Color.Blue) // 背景(在裁剪后)
.clickable { /* 点击 */ }
.testTag("card")常用 Modifier 分类:
| 分类 | 示例 |
|---|---|
| 布局 | size padding fillMaxWidth aspectRatio |
| 绘制 | background border graphicsLayer |
| 交互 | clickable pointerInput draggable |
| 语义 | semantics testTag contentDescription |
记忆技巧:布局管"位置尺寸"、绘制管"外观"、交互管"响应"、语义管"无障碍与测试"——大部分场景按这个思路找对应 Modifier 即可。
5. Compose 与 View 互操作
Compose 与 View 体系可以双向嵌入:在 Compose 里通过 AndroidView 把传统 View 包成可组合函数;在 View 体系里通过 ComposeView + setContent 嵌入 Compose 内容。项目迁移期的"新页面 Compose、旧页面 View"混合架构全靠这两个桥:
// ① Compose 中嵌入 View(AndroidView)
@Composable
fun AndroidViewInCompose() {
AndroidView(factory = { context ->
TextView(context).apply {
text = "来自 View 体系"
}
}, update = { view ->
view.text = "更新后的文本"
})
}
// ② View 中嵌入 Compose(ComposeView)
val composeView = findViewById<ComposeView>(R.id.compose_view)
composeView.setContent {
MaterialTheme {
Text("来自 Compose")
}
}6. 状态提升(State Hoisting)
状态提升指的是"把状态从子组件提升到父组件":子组件变成纯展示(接收值 + 回调),状态归父组件持有。这样做的收益是单一数据源——同一个状态在界面上只有一个出处,测试、复用、调试都更简单:
// 无状态可组合:状态由调用方持有(单一数据源)
@Composable
fun NameInput(name: String, onNameChange: (String) -> Unit) {
TextField(
value = name,
onValueChange = onNameChange
)
}
// 调用方持有状态
@Composable
fun Screen() {
var name by remember { mutableStateOf("") }
NameInput(name = name, onNameChange = { name = it })
}优点:可测试、可复用、状态可预测。
7. 高频面试题
Q1:Compose 和 View 体系的区别? A:声明式 vs 命令式;UI 由状态驱动自动更新 vs 手动操作控件; Compose 无 findViewById、无 XML 布局;性能上 Compose 重组只更新受影响部分。
Q2:什么是重组?如何避免不必要的重组? A:状态变化时 Compose 重新执行受影响的可组合函数。避免方式: remember 缓存、derivedStateOf 派生状态、rememberUpdatedState、 拆分状态作用域、稳定参数(immutable 类)。
Q3:remember 和 mutableStateOf 的关系? A:mutableStateOf 创建可观察状态;remember 在重组间保留状态值。 组合:remember { mutableStateOf(0) }。rememberSaveable 可在配置变化 (旋转)后保留。
Q4:Compose 如何与 View 互操作? A:Compose 中用 AndroidView 嵌入传统 View;View 中用 ComposeView + setContent 嵌入 Compose;Activity/Fragment 可直接 setContent { }。
Q5:LazyColumn 和 Column 的区别? A:LazyColumn 只组合可见项(虚拟化,类似 RecyclerView),适合长列表; Column 组合所有子项,适合少量固定内容。用错会导致性能问题。
8. 小结
- 声明式:UI = f(state),状态驱动重组。
- 可组合函数:@Composable + 参数复用 + 无副作用。
- Modifier 链式修饰,顺序敏感。
- 状态提升保证单一数据源。
- 学习路径:核心概念 → 状态管理 → 布局 → 动画 → 性能优化。