2、Composable 函数:@Composable 注解、函数签名与规范、预览 (Preview) 的使用
好,咱们正式开始写 Compose 代码了。这一节我带你彻底搞懂 Composable 函数——说白了,它就是 Compose 世界里最基础的积木块。你写的每一个界面元素,最终都会是一个 Composable 函数。
2.1 @Composable 注解:它到底干了什么?
先看一个最简单的例子:
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}
这个 @Composable 注解,就是告诉 Kotlin 编译器:「嘿,这个函数不是普通函数,它要在 Compose 的运行时环境里执行。」
我个人习惯把 Composable 函数想象成一个「配方描述」,而不是「烹饪过程」。你写的是「我要显示一段文字」,而不是「创建 TextView、设置文字、添加到布局」这一串指令。这就是声明式 UI 的核心思想。
关键理解:@Composable 注解会改变函数的编译方式。编译器会为它生成额外的代码,用于支持重组(Recomposition)、状态追踪等机制。没有这个注解,函数就是一个普通函数,无法在 Compose 上下文中使用。
我在项目中遇到过一个问题:有同事把 @Composable 加到了一个返回 Boolean 的工具函数上,结果编译报错。嗯,这里要注意——@Composable 不是万能标签,它只应该用在描述 UI 的函数上。
2.2 函数签名与规范
Composable 函数有自己的一套「江湖规矩」。我总结了几个核心点:
2.2.1 命名规范
- 首字母大写:Composable 函数名用 PascalCase,比如
UserProfileCard、LoginButton。这跟普通函数的小驼峰命名不一样。 - 名词为主:函数名描述的是「这是什么」,而不是「做什么」。比如
LoadingIndicator而不是showLoading。 - 避免动词前缀:不要写
createUserCard、buildHeader,直接写UserCard、Header。
我的习惯:把 Composable 函数当作「UI 组件」来命名。如果你觉得这个名字听起来像是一个可复用的组件,那就对了。
2.2.2 参数规范
Composable 函数的参数设计,直接影响组件的复用性。我建议遵循以下原则:
| 原则 | 说明 | 示例 |
|---|---|---|
| 必要参数在前 | 必须传入的数据放在前面 | fun UserCard(name: String, age: Int, modifier: Modifier = Modifier) |
| Modifier 放最后 | 这是 Compose 官方推荐的惯例 | 所有可选的样式参数用 Modifier 统一管理 |
| 提供默认值 | 非必要参数尽量给默认值 | isEnabled: Boolean = true |
| 回调用 lambda | 事件回调用函数类型参数 | onClick: () -> Unit = {} |
2.2.3 函数体规范
Composable 函数体里,有几个「潜规则」:
- 不要有副作用:不要在 Composable 函数里直接执行网络请求、数据库写入等操作。这些应该交给 ViewModel 或协程。
- 保持幂等:同一个参数组合,多次调用应该产生相同的 UI 描述。
- 尽量短小:一个 Composable 函数最好不超过 50 行。如果太长,拆成子组件。
我曾经踩过的坑:在 Composable 函数里直接调用了 viewModel.fetchData(),结果每次重组都触发网络请求。用户界面疯狂刷新,后台请求也炸了。后来才意识到,这种副作用应该放在 LaunchedEffect 或 remember 里。
2.3 预览 (Preview):所见即所得的调试利器
写 UI 最怕什么?改一行代码,跑一遍模拟器,等两分钟。Compose 的预览功能就是为了解决这个痛点。
2.3.1 基本用法
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
Greeting(name = "Android")
}
加上 @Preview 注解后,Android Studio 的预览面板里就能直接看到效果。你想想看,改完代码立刻看到 UI 变化,这体验比 XML 布局爽多了。
2.3.2 预览参数详解
| 参数 | 类型 | 说明 |
|---|---|---|
| showBackground | Boolean | 是否显示背景色,默认 false |
| backgroundColor | Long | 自定义背景色,比如 0xFFFF0000 是红色 |
| showSystemUi | Boolean | 是否显示状态栏、导航栏等系统 UI |
| uiMode | Int | UI 模式,比如夜间模式 Configuration.UI_MODE_NIGHT_YES |
| locale | String | 指定语言环境,比如 "zh-rCN" |
| fontScale | Float | 字体缩放比例,默认 1.0 |
| device | String | 指定预览设备,比如 "id:specs_phone_6_7" |
| widthDp | Int | 限制预览宽度(单位 dp) |
| heightDp | Int | 限制预览高度(单位 dp) |
2.3.3 多预览与分组
实际开发中,我经常需要同时看多个状态。比如按钮的普通态、加载态、禁用态:
@Preview(name = "普通态", showBackground = true)
@Composable
fun SubmitButtonPreview() {
SubmitButton(text = "提交", isLoading = false, enabled = true)
}
@Preview(name = "加载中", showBackground = true)
@Composable
fun SubmitButtonLoadingPreview() {
SubmitButton(text = "提交", isLoading = true, enabled = false)
}
@Preview(name = "禁用态", showBackground = true)
@Composable
fun SubmitButtonDisabledPreview() {
SubmitButton(text = "提交", isLoading = false, enabled = false)
}
这样在预览面板里就能同时看到三个状态,不用来回改代码。我个人习惯给每个预览函数加上 name 参数,方便区分。
小技巧:用 @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) 可以快速检查夜间模式下的 UI 效果。我曾经因为没检查夜间模式,上线后被用户吐槽「亮瞎眼」——嗯,从那以后我再也不敢偷懒了。
2.4 核心知识体系
下面这张图帮你梳理了本章的核心脉络:
2.5 避坑指南
最后分享几个我实际开发中遇到的坑:
- 预览不刷新:有时候改了代码预览没变化,试试 Build → Clean Project,或者重启 Android Studio。我遇到过好几次,多半是 IDE 缓存问题。
- 预览函数不能有参数:
@Preview注解的函数必须是无参的。如果需要传参,在函数内部写死测试数据。 - 不要在预览里写业务逻辑:预览函数只负责展示 UI,不要在里面调接口、读数据库。预览环境没有完整的 Android 上下文。
- Modifier 顺序影响效果:
.size().background()和.background().size()结果可能不同。我刚开始学的时候被这个坑过好几次。
一句话总结:Composable 函数是 Compose 世界的基石。写好它,你的声明式 UI 之路就成功了一半。多写、多预览、多总结,慢慢就会形成肌肉记忆。
公众号:蓝海资料掘金营,微信deep3321