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,比如 UserProfileCardLoginButton。这跟普通函数的小驼峰命名不一样。
  • 名词为主:函数名描述的是「这是什么」,而不是「做什么」。比如 LoadingIndicator 而不是 showLoading
  • 避免动词前缀:不要写 createUserCardbuildHeader,直接写 UserCardHeader

我的习惯:把 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(),结果每次重组都触发网络请求。用户界面疯狂刷新,后台请求也炸了。后来才意识到,这种副作用应该放在 LaunchedEffectremember 里。

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 核心知识体系

下面这张图帮你梳理了本章的核心脉络:

Composable 函数 @Composable 注解 编译时生成重组代码 声明式 UI 描述 非普通函数 函数签名与规范 PascalCase 命名 Modifier 放最后 无副作用、幂等 @Preview 预览 多状态同时预览 夜间模式检查 设备/语言模拟 核心原则 描述 UI 而非操作 UI · 保持纯函数 · 善用预览

2.5 避坑指南

最后分享几个我实际开发中遇到的坑:

  1. 预览不刷新:有时候改了代码预览没变化,试试 Build → Clean Project,或者重启 Android Studio。我遇到过好几次,多半是 IDE 缓存问题。
  2. 预览函数不能有参数@Preview 注解的函数必须是无参的。如果需要传参,在函数内部写死测试数据。
  3. 不要在预览里写业务逻辑:预览函数只负责展示 UI,不要在里面调接口、读数据库。预览环境没有完整的 Android 上下文。
  4. Modifier 顺序影响效果.size().background().background().size() 结果可能不同。我刚开始学的时候被这个坑过好几次。

一句话总结:Composable 函数是 Compose 世界的基石。写好它,你的声明式 UI 之路就成功了一半。多写、多预览、多总结,慢慢就会形成肌肉记忆。


公众号:蓝海资料掘金营,微信deep3321