基本组件与函数签名
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(text = "Hello $name!", modifier = modifier.padding(16.dp))
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
Greeting("Android")
}
@Composable标记函数可在 Compose 运行时参与 UI 组合。- 参数默认不可变,建议使用
modifier提供扩展能力。
状态管理
remember 与 mutableStateOf
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("点击次数:$count")
}
}
remember在重组过程中保留状态。- 使用
by委托简化读写。
rememberSaveable
var text by rememberSaveable { mutableStateOf("") }
- 可在配置变更(旋转)后保留状态。
StateFlow + collectAsStateWithLifecycle
@Composable
fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) {
val state by viewModel.uiState.collectAsStateWithLifecycle()
when (state) {
is UiState.Loading -> Loading()
is UiState.Success -> ArticleList(state.data)
is UiState.Error -> ErrorView(state.error)
}
}
布局与 Modifier
- 常用组合:
Column、Row、Box。 - 使用
Modifier.padding()、Modifier.fillMaxWidth()等链式调用。
val listModifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
LazyColumn(modifier = listModifier) {
items(articles) { article -> ArticleItem(article) }
}
副作用处理
| API | 用途 |
|---|---|
LaunchedEffect | 启动协程执行一次性任务 |
DisposableEffect | 注册/注销监听 |
rememberUpdatedState | 捕捉最新 lambda,避免闭包问题 |
SideEffect | 在每次成功重组后与外部世界同步 |
@Composable
fun AutoRefresh(onRefresh: () -> Unit) {
val latestOnRefresh by rememberUpdatedState(onRefresh)
LaunchedEffect(Unit) {
while (true) {
delay(5_000)
latestOnRefresh()
}
}
}
动画
val alpha by animateFloatAsState(
targetValue = if (visible) 1f else 0f,
animationSpec = tween(durationMillis = 300)
)
AnimatedVisibility(visible = expanded) {
Text("详情内容")
}
Compose + Navigation
@Composable
fun AppNavGraph(navController: NavHostController = rememberNavController()) {
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(onNavigate = { navController.navigate("detail/$it") }) }
composable(
route = "detail/{id}",
arguments = listOf(navArgument("id") { type = NavType.LongType })
) { backStackEntry ->
val id = backStackEntry.arguments?.getLong("id") ?: return@composable
DetailScreen(id)
}
}
}
性能优化
- 避免在 Composable 中执行耗时操作,使用
LaunchedEffect或 ViewModel。 - 将可组合函数
@Stable或使用immutable类型,减少不必要重组。 - 使用
remember缓存昂贵对象,如Painter、CoroutineScope。 - 在
LazyList中为 item 提供稳定的key。
LazyColumn {
items(items = messages, key = { it.id }) { message ->
MessageItem(message)
}
}
测试策略
@RunWith(AndroidJUnit4::class)
class HomeScreenTest {
@get:Rule
val composeRule = createAndroidComposeRule<MainActivity>()
@Test
fun showLoading() {
composeRule.onNodeWithText("加载中").assertExists()
}
}
- 使用
createComposeRule/createAndroidComposeRule构建测试环境。 - 调用
onNode系列方法查找组件、断言状态。
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 重组频繁 | 状态对象不稳定 | 使用 remember、derivedStateOf |
| 滚动跳动 | LazyList item 未提供稳定 key | 在 items 中提供唯一 key |
| 状态丢失 | 使用 remember 而非 rememberSaveable | 对需持久状态使用 rememberSaveable |
实战案例:Compose 和 Flutter 的声明式 UI 是亲兄弟
Compose 的心智模型和 Flutter 几乎一模一样:@Composable ≈ StatelessWidget/StatefulWidget,remember ≈ State,State<T> ≈ ValueNotifier,LazyColumn ≈ ListView.builder。我第一次看 Compose 代码时,最大的感觉是”这我熟”。
但有两处关键差异,Flutter 开发者转 Compose 必须注意:
- 重组(recomposition)比 Flutter 的 rebuild 更激进。Flutter 的
setState只 rebuild 当前 subtree,Compose 的重组范围由编译器插件决定,写不好会大面积重组。官方为此给了derivedStateOf(≈ Flutter 的ValueListenableBuilder做细粒度)和@Stable/@Immutable注解——Flutter 里没有对等物,因为 Dart 没有编译器插件这层。 - 副作用 API 更显式。
LaunchedEffect≈initState里开协程,DisposableEffect≈dispose,SideEffect≈addPostFrameCallback。Compose 把”什么时候执行”写进 API 名字,Flutter 靠生命周期方法隐式约定。初学觉得啰嗦,项目大了才知道这是救命设计。
一句话:会 Flutter 的人学 Compose,最快路径是”找对应物”,而不是从零学。两套框架在互相抄作业。
参考:Android 官方文档《Jetpack Compose》思维模型章节;《Compose mental model》
总结
- Jetpack Compose 通过函数式 UI 与 Kotlin 状态管理提升开发效率。
- 熟练掌握
remember、Flow、SideEffect等 API,可构建响应式界面。 - 关注性能与测试,才能在大型项目中稳定落地 Compose。
下一篇将探讨 Kotlin 在架构组件与多模块协同中的实践。
