1. Compose 初体验:声明式UI到底是什么?
说实话,我第一次接触 Compose 的时候,心里是有点抗拒的。做了七八年 Android 开发,XML + findViewById 那套流程早就刻在肌肉记忆里了。但当我真正写完第一个 @Composable 函数,看到模拟器上那个 "Hello World" 的时候——嗯,我承认,这感觉确实不一样。
这一章,咱们不聊虚的。直接上手,看看声明式UI到底是个什么玩意儿,跟传统 View 系统比有什么优势,最后再搭环境跑一个 Hello World。
什么是声明式UI?
先问一个问题:你平时怎么写界面?
传统方式是这样的:
- 先在 XML 里画一个布局
- 然后在 Activity 里 findViewById
- 再写一堆 setText、setOnClickListener
- 数据变了?再手动调用 setText 更新
这叫做「命令式UI」——你告诉系统每一步该做什么。
而声明式UI呢?
你只需要描述界面「应该长什么样」,剩下的交给框架。
核心思想:UI = f(state)。界面是状态的一个函数。状态变了,界面自动重新绘制。
举个例子。你写一个计数器:
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("点击了 $count 次")
}
}
看到没?没有 findViewById,没有 setText。count 变了,Text 自动更新。这就是声明式的魅力。
我个人习惯:刚开始学 Compose 的时候,别想太多「底层怎么实现的」。先把它当成一个「描述UI的工具」来用,慢慢就理解了。
Compose 与传统 View 系统的对比
我做过一个项目,用传统 View 写了一个复杂的表单页面。大概有 30 多个输入框,各种联动校验。那代码量……啧,光是 findViewById 就写了小一百行。后来用 Compose 重写了一遍,代码量直接砍半。
咱们来做个对比:
| 对比维度 | 传统 View 系统 | Jetpack Compose |
|---|---|---|
| UI 构建方式 | XML + 代码 | 纯 Kotlin 代码 |
| 数据更新 | 手动调用 setText / setVisibility | 状态变化自动重组 |
| 布局层级 | 容易嵌套过深,性能下降 | 扁平化,性能更好 |
| 代码可读性 | UI 和逻辑分离,但跳来跳去 | UI 和逻辑在一起,一目了然 |
| 学习曲线 | 低,但写复杂 UI 很痛苦 | 初期有点别扭,上手后很爽 |
| 预览 | Layout Editor 预览 | @Preview 注解,实时预览 |
你想想看,传统方式最大的问题是什么?是「状态和UI不同步」。数据变了,你忘了更新界面,bug 就来了。Compose 从根本上解决了这个问题——状态是唯一的数据源,UI 自动跟着状态走。
我曾经踩过一个坑:在传统 View 项目里,有个页面需要根据用户权限显示/隐藏某些按钮。我写了七八个 if-else 来控制 visibility。后来需求变了,漏改了一个地方,线上出了权限泄露。用 Compose 的话,你只需要在状态里维护一个权限字段,UI 自动响应,根本不会漏。
Compose 的核心架构
为了让你更直观地理解 Compose 的工作方式,我画了一张图:
说白了,Compose 就是一个「状态驱动UI」的框架。你只需要关心状态怎么变,UI 会自动跟着变。这跟传统 View 系统那种「手动同步」的思路完全不同。
环境搭建
好,理论说完了,咱们来点实际的。搭环境。
你需要准备的东西不多:
- Android Studio —— 推荐 Arctic Fox 2020.3.1 以上版本
- JDK 11+ —— 这个一般 Android Studio 自带
- Kotlin 插件 —— 通常也是自带的
我建议:直接用 Android Studio 最新稳定版。别折腾老版本,Compose 对 IDE 版本有要求,版本太低连预览都跑不了。
创建项目的步骤:
- 打开 Android Studio,点击 "New Project"
- 选择 "Empty Compose Activity" 模板
- 填写项目名称、包名、保存路径
- Minimum SDK 选 API 21 或以上
- 点击 Finish,等 Gradle 同步完成
同步完成后,你会看到项目里已经有一个 MainActivity.kt 文件。打开它,代码大概是这样的:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
HelloWorldTheme {
Surface(modifier = Modifier.fillMaxSize()) {
Greeting("Android")
}
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
HelloWorldTheme {
Greeting("Android")
}
}
看到 setContent 了吗?这就是 Compose 的入口。它替代了传统 Activity 里的 setContentView(R.layout.activity_main)。
第一个 Hello World
咱们把默认的 "Hello Android" 改成 "Hello World"。顺便加点料:
@Composable
fun Greeting(name: String) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "Hello $name!",
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF1976D2)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "欢迎来到 Compose 的世界",
fontSize = 16.sp,
color = Color.Gray
)
}
}
然后运行一下。嗯,看到模拟器上那个蓝色加粗的 "Hello World" 了吗?这就是你用 Compose 写的第一行 UI。
关键点:注意看 Column、Text、Spacer 这些组件。它们都是函数,不是类。在 Compose 里,一切 UI 都是函数调用。你不需要 new 一个对象,也不需要设置属性——直接传参数就行。
避坑指南
我刚开始用 Compose 的时候,遇到过几个问题,分享给你:
- 预览不显示?检查一下 build.gradle 里有没有加 compose 相关的依赖,还有 kotlin 版本是不是 1.5.0 以上。
- 运行报错?看看 Gradle 版本是不是太老了。Compose 对 Gradle 版本有要求,建议用 7.0 以上。
- 重组太频繁?这个后面会讲,记住一点:不要在 Composable 函数里做耗时操作。
我曾经犯过一个低级错误:在 setContent 外面调用了 Compose 的组件。结果编译报错,提示 "Composable invocations can only happen from the context of a Composable function"。记住,所有 @Composable 注解的函数,只能在另一个 @Composable 函数里调用,或者在 setContent 块里调用。
好了,这一章的内容就到这里。你学会了什么是声明式UI,知道了 Compose 和传统 View 系统的区别,也亲手跑了一个 Hello World。下一章咱们会深入 Compose 的布局系统,看看那些好用的组件到底怎么用。
公众号:蓝海资料掘金营,微信deep3321