第一个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 运行时维护,当状态变化时就重建被影响的节点——不是全量重建,是智能重组。

为了让概念更清晰,我画了一张图:

Compose 应用结构:从 setContent 到 UI 树 setContent { } Greeting("World") Text("Hello, World!") Column / Row 容器 Button、Image 等 自定义 Composable 💡 每个 Composable 函数都是 UI 树的一个节点,由 Compose 运行时管理重组 setContent 是整个树的根节点——应用启动时首次渲染,状态变化时局部重构 入口层 组合层 基础组件层 扩展组合层

表格: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 应用。后面的课程会在这个基础上,一步步加入离线缓存、数据库同步、网络优先降级。地基打牢了,上边盖楼才稳。