Jetpack Compose 入门系列(一):从零搭建到基础控件使用

92 阅读10分钟

Jetpack Compose 入门系列(一):从零搭建到基础控件使用

一、前言

一直用 XML + View 写界面?Jetpack Compose 是你该学的新方向。

1.1 什么是 Compose?

Jetpack Compose 是 Google 在 2021 年正式推出的 Android 原生声明式 UI 框架——它不依赖 XML,而是用 Kotlin 代码直接"描述"界面长什么样。

来做个对比你就明白了。以前用 XML + View 体系写界面,大致是三步走:

// 第一步:在 XML 里定义好布局
// 第二步:在 Activity 里 findViewById 找到控件
// 第三步:调用 setText()、setOnClickListener() 手动更新

这套模式叫命令式 UI——你得像下指令一样告诉程序每一步该做什么。而 Compose 的做法完全不同:

// 你只需要描述:"这里有一个文本,内容是 Hello"
Text(text = "Hello")

你说"界面应该长这样",框架自己判断什么时候该更新、更新哪里。这就是声明式 UI

一句话总结: 命令式是你指挥细节,声明式是你描述结果。门槛更低,代码更少。

用一张图来对比两种模式的工作流程:

flowchart LR
    subgraph 命令式_Imperative
        A[XML 定义布局] --> B[findViewById 找控件]
        B --> C[setText/setOnClickListener 手动更新]
    end
    
    subgraph 声明式_Declarative
        D[Composable 函数描述界面] --> E[状态变化自动触发重组]
    end
    
    命令式_Imperative -.->|vs| 声明式_Declarative

左边命令式:你要自己找到控件、自己更新。右边声明式:你说长什么样,框架管更新。

1.2 Compose 的优势

  • 更少的代码:一个 Text 控件从定义到展示,只需要一行代码
  • 直观的状态管理:状态变化自动触发界面重组(Recompose),无需手动更新
  • 强大的可组合性:小而独立的 Composable 函数自由组合
  • 完全用 Kotlin 编写:可以使用 Kotlin 的所有语言特性(高阶函数、协程等)
  • 与现有 View 体系互通:可以在 Compose 中嵌入 View,也可以在 View 中嵌入 Compose

二、环境搭建

2.1 Android Studio 版本

Compose 需要 Android Studio Arctic Fox(2020.3.1)或更高版本。建议直接使用最新的稳定版 Android Studio, 本文示例代码使用的Android Studio版本是 2025.3.4 Patch 1

2.2 创建支持 Compose 的项目

如果你从模板创建项目,Android Studio 提供了 "Empty Activity" 模板,勾选后会自动配置好所有依赖。

如果是已有项目要接入 Compose,需要手动配置以下内容。

2.3 Gradle 配置

第一步:设置 Kotlin 和 Compose 编译器

在项目根目录的 build.gradle.kts中:

// build.gradle.kts(项目级)
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.kotlin.compose) apply false
}
第二步:声明依赖和指定版本

gradle/libs.versions.toml 声明依赖和指定版本:

// libs.versions.toml
[versions]
agp = "9.2.1"
coreKtx = "1.10.1"
lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.8.0"
kotlin = "2.2.10"
composeBom = "2026.02.01"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
第三步:添加 Compose BOM 和依赖

在模块的 build.gradle.kts 中:

// build.gradle.kts(模块级)
android {
    // 需要启用 Compose
    buildFeatures {
        compose = true
    }
}

dependencies {
    // BOM(Bill of Materials):统一管理 Compose 版本,避免版本冲突
    implementation(platform(libs.androidx.compose.bom))
    
    // UI 基础
      implementation(libs.androidx.compose.ui)
    // UI 图形工具
    implementation(libs.androidx.compose.ui.graphics)
    // UI 预览(只在 debug 模式下需要)
    implementation(libs.androidx.compose.ui.tooling.preview)
    // Material 3 设计系统
    implementation(libs.androidx.compose.material3)
    // Activity Compose(setContent 扩展)
    implementation(libs.androidx.activity.compose)
    // 调试工具
    debugImplementation(libs.androidx.compose.ui.tooling)
}

注意kotlinCompilerExtensionVersion 必须与 Kotlin 版本匹配,具体对应关系可以查看 Compose 与 Kotlin 版本兼容表

BOM 是什么? BOM(Bill of Materials)是一份"材料清单",你在 dependencies 中声明 Compose 库时不用写具体版本号,BOM 会自动为你选择兼容的版本。这样既不用记版本号,也不用担心版本冲突。


三、第一个 Compose 程序

3.1 Hello World

创建一个最简单的 Compose Activity:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // setContent 是 Activity 的扩展函数,用于设置 Compose 界面
        setContent {
            // 这里写 Compose 代码
            Text(text = "Hello Compose!")
        }
    }
}

就这么简单。setContentactivity-compose 提供的扩展函数,它接受一个 @Composable 函数作为参数——所有 Compose 代码都在这里编写。

整个调用链的关系可以这样理解:

flowchart TB
    A[Activity.onCreate] --> B[setContent 扩展函数]
    B --> C[Composable 作用域建立]
    C --> D[Text<br/>Hello Compose!]
    
    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#e8f5e9
    style D fill:#fce4ec

3.2 Composable 函数

Text 是一个Composable 函数。任何被 @Composable 注解标记的函数都称为 Composable 函数:

@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}

Composable 函数有两个硬性规则,刚接触时特别容易忽略:

  1. 函数名必须以大写字母开头。 这不是什么玄学——Compose 利用这个约定来区分"这是一个 Composable 界面组件"和"这是一个普通工具函数"。如果你用小写开头,编译器不会报错,但团队约定和 IDE 提示都会默认按大写走
  2. 只能在另一个 Composable 函数的作用域中调用 Composable 函数。 简单说,setContent { } 的花括号里、或者在某个 @Composable fun xxx() 里,才能调用别的 Composable。在普通 Kotlin 函数里直接调用 Text() 会编译报错

所以上面的 Activity 可以改写成:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Greeting(name = "Compose")
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}

3.3 主题和预览

为了让界面更好看且贴近 Material Design,通常会用 MaterialTheme 包裹:

setContent {
    MaterialTheme {
        Greeting(name = "Compose")
    }
}

另外,Compose 提供了预览功能——用 @Preview 注解标注一个 Composable 函数,就可以在 Android Studio 的 Design 面板中实时看到效果,无需运行到真机:

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    MaterialTheme {
        Greeting(name = "Compose")
    }
}

@Preview 只能标注不接收参数或参数有默认值的 Composable 函数,因为预览时无法传入参数。


四、基础控件详解

接下来逐个介绍最常用的 Compose 控件。每种控件我都会给出主要参数说明和完整的示例代码。

4.1 Text:文本显示

Text 是最基础的控件,用来显示文字:

@Composable
fun TextExample() {
    Text(
        text = "Hello Compose",
        color = Color.Red,
        fontSize = 20.sp,
        fontWeight = FontWeight.Bold,
        textAlign = TextAlign.Center,
        maxLines = 2,
        overflow = TextOverflow.Ellipsis
    )
}

常用参数:

参数类型说明
textString / AnnotatedString要显示的文本
colorColor文字颜色
fontSizeTextUnit(sp)字体大小,用 .sp 单位
fontWeightFontWeight字重(Bold、Light、Normal 等)
textAlignTextAlign对齐方式(Start、Center、End 等)
maxLinesInt最大行数
overflowTextOverflow超出时的处理方式(Ellipsis 省略号、Clip 裁剪)
styleTextStyle统一样式设置,包含上述所有属性

单位说明:Compose 中使用 .dp(独立密度像素)表示尺寸,.sp(缩放独立像素)表示字体大小,这和 XML 中的 dp/sp 含义一致。

以上全部参数只是 Text 的一部分,更多参数可以在 IDE 中按 Ctrl+P(Windows)或 Command+P(Mac)查看。

4.2 Button:按钮

Button 是 Material 3 的按钮组件:

@Composable
fun ButtonExample() {
    Button(
        onClick = { /* 点击回调 */ },
        enabled = true,
        colors = ButtonDefaults.buttonColors(
            containerColor = Color.Blue,
            contentColor = Color.White
        )
    ) {
        // Button 的内容区域,可以放任何 Composable
        Text(text = "点击我")
    }
}

关键点:

  • Buttoncontent 参数(花括号内的内容)可以是 任意 Composable,不一定是 Text。你可以放 Image、Icon、Row 等
  • onClick 是必须传入的参数,定义了点击事件
  • enabled 控制按钮是否可点击

其他按钮变体:

// 描边按钮
OutlinedButton(onClick = { }) {
    Text("Outlined Button")
}

// 文字按钮
TextButton(onClick = { }) {
    Text("Text Button")
}

// 带图标的按钮
Button(onClick = { }) {
    Icon(Icons.Default.Add, contentDescription = "添加")
    Spacer(modifier = Modifier.width(8.dp))
    Text("添加")
}

4.3 TextField:文本输入框

文本输入框用来接收用户输入:

@Composable
fun TextFieldExample() {
    // 定义一个可变状态来存储输入内容
    var input by remember { mutableStateOf("") }
    
    OutlinedTextField(
        value = input,
        onValueChange = { input = it },
        label = { Text("用户名") },
        placeholder = { Text("请输入用户名") },
        singleLine = true,
        isError = input.length > 10,
        supportingText = {
            if (input.length > 10) {
                Text("用户名不能超过10个字符", color = MaterialTheme.colorScheme.error)
            }
        }
    )
}

关键概念——State(状态):

你可能注意到了 remembermutableStateOf——这是 Compose 中管理状态的机制。简单来说:

  • mutableStateOf("") 创建了一个可观察的状态
  • remember 保证这个状态在重组(界面刷新)时不被重置
  • onValueChange 在每次输入变化时回调,我们把新值更新到状态中

关于 State 的详细讲解在第二篇文章中,这里先记住用法即可。

常用参数:

参数说明
value当前输入框的内容
onValueChange内容变化时的回调
label标签(显示在输入框上方或内部)
placeholder占位提示文字
singleLine是否单行输入
isError是否显示错误状态
keyboardOptions键盘配置(数字键盘、邮箱键盘等)
visualTransformation视觉变换(如密码遮罩 PasswordVisualTransformation)

密码输入框示例:

@Composable
fun PasswordField() {
    var password by remember { mutableStateOf("") }
    
    OutlinedTextField(
        value = password,
        onValueChange = { password = it },
        label = { Text("密码") },
        visualTransformation = PasswordVisualTransformation(),
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
    )
}

4.4 Image:图片显示

Image 用来显示图片资源:

@Composable
fun ImageExample() {
    Image(
        painter = painterResource(id = R.drawable.my_image),
        contentDescription = "图片描述",
        modifier = Modifier.size(200.dp),
        contentScale = ContentScale.Crop
    )
}

常用参数:

参数说明
painter图片绘制器,painterResource() 加载本地图片
contentDescription无障碍描述,纯装饰性图片可传 null
contentScale图片缩放模式(Crop 裁剪、Fit 适应、Fill 填充等)
alpha透明度(0f ~ 1f)

常用 ContentScale 模式:

ContentScale.Crop      // 裁剪居中(类似 XML 的 centerCrop)
ContentScale.Fit       // 等比缩放,可能留白(类似 fitCenter)
ContentScale.FillBounds // 拉伸填充(类似 fitXY)
ContentScale.Inside    // 缩小到容器内,不放大

painterResource() 会自动适配不同密度的图片资源,加载方式与 XML 中的 R.drawable.xxx 一致。 如果是从网络加载图片,需要使用 Coil 等三方库的 AsyncImage,这是后话。

4.5 Spacer:占位间距

Spacer 用来在布局中插入空白区域:

Spacer(modifier = Modifier.height(16.dp))  // 垂直间距
Spacer(modifier = Modifier.width(8.dp))    // 水平间距

五、Modifier 详解

Modifier 是 Compose 中最核心的概念之一。它用来装饰或增强一个 Composable 的外观和交互行为。

5.1 使用方式

几乎所有 Composable 都接收一个可选的 modifier 参数:

Text(
    text = "Hello",
    modifier = Modifier
        .padding(16.dp)          // 设置内边距
        .background(Color.Yellow) // 设置背景色
        .clickable { /* 点击 */ }  // 设置点击
        .fillMaxWidth()          // 填充最大宽度
)

5.2 链式调用与顺序

Modifier 通过链式调用组合多个行为,而且顺序很重要

// 顺序一:先 padding 再 background
@Composable
fun ModifierOrder1() {
    Text(
        text = "Hello",
        modifier = Modifier
            .padding(16.dp)       
            .background(Color.Red) 
    )
}

// 顺序二:先 background 再 padding
@Composable
fun ModifierOrder2() {
    Text(
        text = "Hello",
        modifier = Modifier
            .background(Color.Red) 
            .padding(16.dp)   
    )
}

两种顺序的效果完全不同:

  • 顺序一:padding 区域是透明的(padding 在 background 外部)
  • 顺序二:padding 区域也会被染成红色(padding 在 background 内部)

记住原则: Modifier 的执行顺序是从左到右的,每个 Modifier 会包裹前一个 Modifier 的结果。

5.3 常用 Modifier 速查

Modifier作用
.padding(all = 16.dp)内边距
.padding(start = 8.dp, top = 16.dp)指定方向的内边距
.size(48.dp)宽高设为固定值
.width(100.dp)宽度固定
.height(100.dp)高度固定
.fillMaxWidth()撑满父容器宽度
.fillMaxHeight()撑满父容器高度
.fillMaxSize()撑满父容器
.background(Color.Blue)背景色
.background(Color.Blue, shape = RoundedCornerShape(8.dp))圆角背景
.clickable { }点击事件
.border(width = 1.dp, color = Color.Gray)边框
.border(width = 1.dp, color = Color.Gray, shape = RoundedCornerShape(8.dp))圆角边框
.clip(RoundedCornerShape(8.dp))裁剪为圆角
.offset(x = 10.dp, y = 10.dp)偏移位置
.alpha(0.5f)透明度
.rotate(45f)旋转角度
.weight(1f)在 Row/Column 中分配权重(类似 LinearLayout 的 weight)

六、实战:一个简单登录界面

把上面的知识串起来,写一个完整的登录界面:

@Composable
fun LoginScreen() {
    // 状态
    var username by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }
    
    // Column 是垂直布局容器,相当于 LinearLayout(vertical)
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(24.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        // 标题
        Text(
            text = "欢迎登录",
            fontSize = 28.sp,
            fontWeight = FontWeight.Bold,
            color = MaterialTheme.colorScheme.primary
        )
        
        Spacer(modifier = Modifier.height(32.dp))
        
        // 用户名输入框
        OutlinedTextField(
            value = username,
            onValueChange = { username = it },
            label = { Text("用户名") },
            singleLine = true,
            modifier = Modifier.fillMaxWidth()
        )
        
        Spacer(modifier = Modifier.height(16.dp))
        
        // 密码输入框
        OutlinedTextField(
            value = password,
            onValueChange = { password = it },
            label = { Text("密码") },
            singleLine = true,
            visualTransformation = PasswordVisualTransformation(),
            modifier = Modifier.fillMaxWidth()
        )
        
        Spacer(modifier = Modifier.height(24.dp))
        
        // 登录按钮
        Button(
            onClick = { /* 执行登录 */ },
            modifier = Modifier
                .fillMaxWidth()
                .height(50.dp),
            enabled = username.isNotBlank() && password.isNotBlank()
        ) {
            Text(text = "登录", fontSize = 18.sp)
        }
    }
}

把这个 Composable 放到 Activity 的 setContent 中:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                LoginScreen()
            }
        }
    }
}

效果说明:居中显示的"欢迎登录"标题,两个输入框,底部一个登录按钮。用户名或密码为空时按钮为禁用状态。

整个登录界面的组件树结构如下:

flowchart TB
    A[LoginScreen] --> B[Column<br/>垂直居中]
    B --> C[Text<br/>欢迎登录]
    B --> D[Spacer 32dp]
    B --> E[OutlinedTextField<br/>用户名]
    B --> F[Spacer 16dp]
    B --> G[OutlinedTextField<br/>密码]
    B --> H[Spacer 24dp]
    B --> I[Button<br/>登录<br/>enabled 受状态控制]

七、注意事项与小技巧

7.1 避免在 Composable 函数中写耗时操作

// ❌ 错误:在 Composable 中直接执行耗时操作
@Composable
fun BadExample() {
    val result = someHeavyComputation() // Compose 可能频繁重组,导致卡顿
    Text(text = result)
}

// ✅ 正确:使用 LaunchedEffect 或回调
@Composable
fun GoodExample() {
    var result by remember { mutableStateOf("") }
    LaunchedEffect(Unit) {
        result = someHeavyComputation() // 在协程中执行
    }
    Text(text = result)
}

7.2 Modifier 参数尽量暴露出去

// ✅ 好的实践:将 modifier 作为参数并提供默认值
@Composable
fun MyCustomButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Button(
        onClick = onClick,
        modifier = modifier
    ) {
        Text(text = text)
    }
}

这样调用方可以自由定制按钮的样式和行为。

7.3 资源引用

在 Compose 中获取颜色、字符串、尺寸等资源:

// 字符串资源
val appName = stringResource(id = R.string.app_name)

// 颜色资源(如果使用了 Material3 的话推荐直接用 MaterialTheme)
val customColor = colorResource(id = R.color.custom_color)

// 尺寸资源
val dimen = dimensionResource(id = R.dimen.custom_dimen)

八、总结

这篇文章我们学习了:

  1. Compose 的核心概念:声明式 UI 与命令式 UI 的区别
  2. 环境搭建:Gradle 配置、BOM 的使用
  3. Composable 函数:基本规范和注意事项
  4. 基础控件:Text、Button、OutlinedTextField、Image、Spacer
  5. Modifier:链式调用、执行顺序、常用 Modifier 速查
  6. 实战:一个完整的登录界面

这些知识足以让你写出简单的 Compose 页面了。下一篇文章我们将进一步探讨 布局系统(Column、Row、Box、LazyColumn 等)和 State 管理(remember、状态提升、StateFlow 收集),这些是写出复杂交互界面的基础。


如果你在搭建过程中遇到任何问题,欢迎在评论区留言交流。****