第一个Compose应用:Hello World
说实话,每次学新框架,我第一件事就是跑个 Hello World。这不是形式主义——它能帮你验证环境、熟悉工具链、感受“代码跑通”的踏实感。离线优先的 Compose 也不例外,咱们从最干净的起点开始。
创建项目:两种方式随你选
我个人习惯用 Android Studio 的模板新建,但有些朋友喜欢命令行。都行,结果一样。
- Android Studio 方式:File → New → New Project → Empty Activity(Compose 版本)。选中「Use Jetpack Compose」。
- 命令行方式:用 Gradle 直接初始化。不过你想想看,做离线优先学习,IDE 会更直观。
项目建好后,确保 build.gradle.kts 里这些依赖到位:
// build.gradle.kts (Module :app)
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
compileSdk = 34
defaultConfig {
minSdk = 24
targetSdk = 34
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.8"
}
}
dependencies {
implementation("androidx.compose.ui:ui:1.6.0")
implementation("androidx.compose.material3:material3:1.2.0")
implementation("androidx.compose.ui:ui-tooling-preview:1.6.0")
implementation("androidx.activity:activity-compose:1.8.2")
}
小提醒:版本号别照抄本文——我写的时候是 2025 年初,你看到的版本可能已经更新。建议去 官方版本页 查最新。
认识核心函数:setContent 和 Composable
打开 MainActivity.kt,你会看到类似这样的骨架:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// 在这里写 Compose 代码
}
}
}
setContent 是 Compose 世界的入口。它接收一个 Composable lambda——说白了,就是一块“可组合的 UI 片段”。
写第一个 Composable 函数
我习惯把 UI 拆成独立函数。来,写一个 Greeting:
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}
然后在 setContent 里调用它:
setContent {
Greeting("World")
}
跑起来!模拟器上会显示 Hello, World!。嗯,就这么简单。但这里面有两条关键规则:
- @Composable 注解:告诉 Compose 这个函数可以生成 UI。没有它,编译器会报错。
- 函数名大写开头:这不是强制语法,但 Compose 社区约定——可组合函数用 PascalCase,一眼就能和普通函数区分。
一个小习惯:我给自己定了条规矩——每个屏幕至少拆成 3 个 Composable:一个容器、一个列表、一个条目。哪怕 Hello World 只有一行字,我也先练好这个结构。
预览:不动模拟器也能看效果
每次改代码都跑模拟器?那太慢了。Compose 提供了预览功能:
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
Greeting("World")
}
在 Android Studio 右侧点击 Split 或 Design 视图,就能看到实时预览。我曾经项目跑 CI 时全靠预览调 UI,省下大把时间。
注意:预览函数不能带参数(除非有默认值)。所以我把 name 写死了。如果参数复杂,我一般做一个 PreviewContainer 把固定参数传进去。
理解 Compose UI 树
传统 View 系统用 XML 描述界面,Compose 用代码直接描述。你写的每个 Text、Button、Column 都变成 UI 树上的节点。这个树由 Compose 运行时维护,当状态变化时就重建被影响的节点——不是全量重建,是智能重组。
为了让概念更清晰,我画了一张图:
表格:Hello World 涉及的核心概念
| 概念 | 作用 | 我的经验 |
|---|---|---|
| setContent | Activity 中启动 Compose 的入口 | 其实是 Activity.setContentView() 的 Compose 变体。以前写 XML 时总忘这步,现在用 setContent 反而更直观。 |
| @Composable | 标记函数为可组合单元 | 记得加上 @Composable,否则编译器会把你当普通函数——编译报错再回来补注解就慢了。 |
| Text() | 基础文本组件 | Text 在 Compose 里几乎替代了 TextView,但用法简洁得多。我常用它显示状态文字、标签、错误信息。 |
| @Preview | 在 IDE 中预览 Composable | 曾有个同事不用预览,每次改 UI 都跑真机,一个上午只调了三个按钮。学会预览之后效率翻倍。 |
| 重组 | 状态变化时只更新受影响的部分 | 这是 Compose 性能的核心。后面章节会深入讲,但你现在要记住:别在 Composable 里写耗时逻辑,会阻塞重组。 |
避坑指南:新手最容易栽的三个坑
1. 忘记在 manifest 里声明 Compose 主题
如果你看到界面一片白板,八成是主题没配。在 AndroidManifest.xml 里给 Activity 加上 android:theme="@android:style/Theme.Material.Light.NoActionBar" 或者用 Compose 的 MaterialTheme。
2. preview 不显示
我曾经花了十分钟发现预览面板是空的——结果只是方法参数没给默认值。预览函数必须无参或有默认值。还有一个隐性规则:如果用了依赖注入或者 Context,预览会失败。我一般把纯 UI 逻辑抽到单独的 Composable 再用预览。
3. import 没配全
Compose 的 API 在 androidx.compose.* 包下,不是 android.widget。IDE 自动导入通常没问题,但有时会推荐错。比如 Text 有两个:一个在 Compose 里,一个在 Material 旧版。记得选 androidx.compose.material3.Text。
上手练习:扩展一下 Hello World
光显示一行字不过瘾。来,加个按钮点击改变文本:
@Composable
fun HelloInteractive() {
var name by remember { mutableStateOf("World") }
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Hello, $name!")
Button(onClick = { name = "Compose" }) {
Text("点我变 Compose")
}
}
}
注意这行 var name by remember { mutableStateOf("World") }——这是 Compose 的状态管理。当你点击按钮,name 变成 "Compose",Text 会自动刷新。这个机制后面章节会系统讲,现在你先见识一下。
调用:
setContent {
HelloInteractive()
}
跑起来,点按钮——文本变了。恭喜,你已经迈出了离线优先 Compose 的第一步。
离线开发提醒:如果你断网学习,Android Studio 的 Gradle sync 会因为缺少依赖缓存失败。解决方案:在首次联网时,用 gradle 命令 ./gradlew assembleDebug --offline 把依赖下载到本地。之后即使断网,只要依赖没换版本,一样能编译运行。
我离线写代码时用的是 Gradle 的 --offline 模式,配合本地 Maven 缓存,完全无障碍。
好了,现在你已经亲手创建并运行了第一个 Compose 应用。后面的课程会在这个基础上,一步步加入离线缓存、数据库同步、网络优先降级。地基打牢了,上边盖楼才稳。