Android Jetpack Compose 入门教程

一、什么是 Jetpack Compose?

Jetpack Compose 是 Android 官方推出的现代化 UI 工具包,它使用声明式的方式来构建用户界面。与传统的 XML 布局相比,Compose 具有以下优势:

  • 声明式 UI:描述 UI 应该是什么样子,而不是如何构建
  • 更少的代码:相比 XML 布局,代码量减少约 50%
  • 实时预览:在 Android Studio 中可以实时预览 UI 效果
  • Kotlin 原生:完全使用 Kotlin 编写,与 Kotlin 语言特性完美结合

1.1 安装或更新 Android Studio

  1. 一台可以联网的电脑
  2. 安装或更新到 最新版的 Android Studio
  3. 选择创建 Empty Activity
    alt text

1.2 Composable 函数

第一步:Composable 函数
Jetpack Compose 是围绕着 Composable 函数建立的。这些函数让你通过描述它的形状和数据依赖性,以编程方式定义你的 UI,而不是专注于 UI 的构建过程。要创建一个 Composable 函数,只需在函数名称中添加 @Composable 注解。
setContent 块定义了一个我们可以调用 Composable 函数的 avtivity 的布局,Composable 函数只能从其他的 Composable 函数中调用
Composable 函数只能从其他 Composable 函数的范围内调用。要写一个 Composable 函数,我们需要添加一个 @Composable 的注解。

1
2
3
4
5
6
7
8
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text("Hello world!")
}
}
}

1.3 预览函数

在 Android Studio 中预览你的函数
Android Studio 可以让你在 IDE 中预览你的 Composable 函数,而不需要将应用下载到 Android 设备或模拟器上。但是有个限制, 需要预览的 Composable 函数必须不能有任何参数。因为这个限制,你不能直接预览 MessageCard() 函数。但是,你可以尝试写第一个叫 PreviewMessageCard() 的函数,它调用带有参数的 MessageCard()。在 @Composable 之前添加 @Preview 注解。

界面
UI 元素是分层次的,元素包含在其他元素中。在 Compose 中,你可以通过从其他 Composable 函数中调用 Composable 函数来建立一个 UI 层次结构。

1
2
3
4
5
6
7
8
9
10
@Composable
fun MessageCard(name: String) {
Text (text = "Hello $name!")
}

@Preview
@Composable
fun PreviewMessageCard() {
MessageCard("Android")
}

二、Compose 基础概念

2.1 @Composable 注解

@Composable 是 Compose 中最核心的注解,它标记一个函数为「可组合函数」。可组合函数用于构建 UI 组件。
如果没有这个注解,你的代码会从 “可组合的 UI 组件” 彻底变成普通的 Kotlin 函数,无法实现 UI 渲染,还会触发一系列编译和运行时问题

1
2
3
4
5
// 这是一个最简单的可组合函数
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}

解析:

  • @Composable 注解告诉编译器这是一个 UI 组件函数
  • 函数名使用帕斯卡命名法(首字母大写)
  • 函数可以接收参数,用于动态显示内容

2.2 Modifier 修饰符

Modifier 是 Compose 中用于修改组件样式、布局和行为的核心工具。
为了修饰和配置一个 Composable, Compose 使用了 modifier,它们允许你改变 Composable 的尺寸、布局、外观或添加高级交互,比如可以让一个元素变得可以点击。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Composable
fun MessageCard(msg: Message) {
Row(
modifier = Modifier.padding(all = 8.dp) // 在我们的 Card 周围添加 padding
) {
Image(
painterResource(id = R.drawable.profile_picture),
contentDescription = "profile picture",
modifier = Modifier
.size(50.dp) // 改变 Image 元素的大小
.clip(CircleShape) // 将图片裁剪成圆形
)
Spacer(Modifier.padding(horizontal = 8.dp)) // 添加一个空的控件用来填充水平间距,设置 padding 为 8.dp
Column {
Text(text = msg.author)
Spacer(Modifier.padding(vertical = 4.dp))
Text(text = msg.body)
}
}
}

常用 Modifier 方法:

  • .padding() - 设置内边距
  • .background() - 设置背景颜色
  • .fillMaxWidth() - 填充父容器宽度
  • .size() - 设置固定尺寸
  • .border() - 设置边框
  • .clickable() - 添加点击事件

2.3 Material design

Jetpack Compose 提供了 Material Design 的实现,我们将使用 Material Design 的风格来改善我们的 MessageCard。
Material Design 是围绕三个元素建立的。颜色(Color)、排版(Typography)、形状(Shape)。
一个 Empty Compose Activity 已经自动为你的项目生成了一个默认的主题,允许你自定义 MaterialTheme。如果你给你的项目命名与 ExamplesTheme 不同,你可以在 ui.theme 包中找到你的自定义主题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ExamplesTheme {
MessageCard(Message("Jetpack Compose 博物馆", "我们开始更新啦"))
}
}
}

@Preview
@Composable
fun PreviewMessageCard() {
ExamplesTheme {
MessageCard(
msg = Message("Jetpack Compose 博物馆", "我们开始更新啦")
)
}
}

颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Row(
modifier = Modifier.padding(all = 8.dp)
) {
Image(
painterResource(id = R.drawable.profile_picture),
contentDescription = "profile picture",
modifier = Modifier
.size(50.dp)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colors.secondary, shape = CircleShape) // 添加边框
)
Spacer(Modifier.padding(horizontal = 8.dp))
Column {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant // 添加颜色
)
Spacer(Modifier.padding(vertical = 4.dp))
Text(text = msg.body)
}
}

排版

1
2
3
4
5
6
7
8
9
10
11
12
Column {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2 // 添加 style
)
Spacer(Modifier.padding(vertical = 4.dp))
Text(
text = msg.body,
style = MaterialTheme.typography.body2 // 添加 style
)
}

形状

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Composable
fun MessageCard(msg: Message) {
Surface(
shape = MaterialTheme.shapes.medium, // 使用 MaterialTheme 自带的形状
elevation = 5.dp,
modifier = Modifier.padding(all = 8.dp)
) {
Row(
modifier = Modifier.padding(all = 8.dp)
) {
Image(
painterResource(id = R.drawable.profile_picture),
contentDescription = "profile picture",
modifier = Modifier
.size(50.dp)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colors.secondary, shape = CircleShape)
)
Spacer(Modifier.padding(horizontal = 8.dp))
Column {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2
)
Spacer(Modifier.padding(vertical = 4.dp))
Text(
text = msg.body,
style = MaterialTheme.typography.body2
)
}
}
}
}

启用深色主题
深色主题(或夜间模式)可以避免明亮的显示,由于支持 Material Design,Jetpack Compose 默认可以处理深色主题。只要是使用了 Material 的颜色后,文本和背景将自动适应黑暗的背景。
让我们添加一个新的预览注解并在手机上或者虚拟机上启用夜间模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Preview(name = "Light Mode")
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true,
name = "Dark Mode"
)
@Composable
fun PreviewMessageCard() {
ExamplesTheme {
MessageCard(
msg = Message("Jetpack Compose 博物馆", "我们开始更新啦")
)
}
}

3.4 列表和动画

创建一个列表消息卡片
使用 Compose 的 LazyColumn 和 LazyRow。
在这个代码片段中,你可以看到 LazyColumn 有一个 items 子项。它接收一个 List 作为参数,它的 lambda 接收一个我们命名为 message 的参数(我们可以随便命名)。 而这个 lambda 将会调用每个 List 中里面提供的 item。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import androidx.compose.foundation.lazy.items

@Composable
fun Conversation(messages: List<Message>) {
LazyColumn {
items(messages) { message ->
MessageCard(msg = message)
}
}
}

@Composable
fun PreviewMessageCard() {
ExamplesTheme {
Conversation(messages = MsgData.messages)
}
}

3.5 可交互的动画效果

我们将会实现当点击一个卡片查看详细内容的时候,使内容的大小和背景颜色都有动画效果。为了存储这个本地 UI 状态,我们需要跟踪一条消息是否已经展开了。为了跟踪这种状态变化,我们必须使用 remember 和 mutableStateOf 函数。
Composable 函数可以通过使用 remember 将本地状态存储在内存中,并跟踪传递给 mutableStateOf 的值的变化。当值被更新时,使用该状态的 Composable 函数(及其子函数)将被自动重新绘制。我们把这称为重组(recomposition)。
通过使用 Compose 的状态 API,如 remember 和 mutableStateOf,任何对状态的改变都会自动更新 UI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Composable
fun MessageCard(msg: Message) {

var isExpanded by remember { mutableStateOf(false) } // 创建一个能够检测卡片是否被展开的变量

Surface(
shape = MaterialTheme.shapes.medium,
elevation = 5.dp,
modifier = Modifier
.padding(all = 8.dp)
.clickable { // 添加一个新的 Modifier 扩展方法,可以让元素具有点击的效果
isExpanded = !isExpanded // 编写点击的事件内容
}
) {
Row(
modifier = Modifier.padding(all = 8.dp)
) {
Image(
painterResource(id = R.drawable.profile_picture),
contentDescription = "profile picture",
modifier = Modifier
.size(50.dp)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colors.secondary, shape = CircleShape)
)
Spacer(Modifier.padding(horizontal = 8.dp))
Column {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2
)
Spacer(Modifier.padding(vertical = 4.dp))
Text(
text = msg.body,
style = MaterialTheme.typography.body2,
// 修改 maxLines 参数,在默认情况下,只显示一行文本内容
maxLines = if (isExpanded) Int.MAX_VALUE else 1,
// Composable 大小的动画效果
modifier = Modifier.animateContentSize()
)
}
}
}
}

使用 isExpanded 来做点其他的事情吧!比如改变卡片的颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建一个能够根据 isExpanded 变量值而改变颜色的变量
val surfaceColor by animateColorAsState(
targetValue = if (isExpanded) Color(0xFFCCCCCC) else MaterialTheme.colors.surface
)

Surface(
shape = MaterialTheme.shapes.medium,
elevation = 5.dp,
modifier = Modifier
.padding(all = 8.dp)
.clickable {
isExpanded = !isExpanded
},
color = surfaceColor
) {
...
...
}

3.6 Alertdialog

1
2
3
4
5
6
7
8
9
10
11
12
13
@Composable
fun AlertDialog(
onDismissRequest: () -> Unit,
confirmButton: () -> Unit,
modifier: Modifier = Modifier,
dismissButton: () -> Unit = null,
title: () -> Unit = null,
text: () -> Unit = null,
shape: Shape = MaterialTheme.shapes.medium,
backgroundColor: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(backgroundColor),
properties: DialogProperties = DialogProperties()
): @Composable Unit

3.7 BaseAlertDialog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
val openDialog = remember { mutableStateOf(true) }
if (openDialog.value) {
BasicAlertDialog(
onDismissRequest = { openDialog.value = false },
modifier = Modifier
.padding(0.dp)
.size(width = 600.dp, height = 200.dp)
// .clip(CircleShape)
// .background(MaterialTheme.colorScheme.surface)
// .border(1.5.dp, MaterialTheme.colorScheme.outline, shape = CircleShape),


) {
// 3. 对话框的自定义内容(核心:你可以放任何Compose组件)
Surface(
modifier = Modifier
.fillMaxWidth(0.8f), // 宽度占屏幕80%
// .padding(24.dp),
shape = RoundedCornerShape(16.dp),
tonalElevation = 8.dp // 阴影
) {
Column(
modifier = Modifier.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// 标题
Text(
text = "BasicAlertDialog",
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.padding(bottom = 12.dp)
)
// 内容
Text(
text = "我是完全自定义的内容,可以放文字、按钮、图片等任意组件",
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(bottom = 24.dp)
)
Spacer(modifier = Modifier.padding(bottom = 2.dp))
// 操作按钮
Button(
onClick = { openDialog.value = false }, // 点击按钮关闭对话框
modifier = Modifier
.align(Alignment.End)
.size(width = 80.dp, height = 50.dp)
.padding(bottom = 0.dp)
) {
Text(text = "关闭")
}
}
}
}
}

三、基础布局组件

3.1 Column - 垂直布局

Column 函数可以让你垂直地排列元素(从上到下)。
用 Row 来水平排列项目,用 Box 来堆叠元素。

1
2
3
4
5
6
7
@Composable
inline fun Column(
modifier: Modifier? = Modifier,
verticalArrangement: Arrangement.Vertical? = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal? = Alignment.Start,
content: (@Composable @ExtensionFunctionType ColumnScope.() -> Unit)?
): Unit

verticalArrangment 和 horizontalAlignment 参数分别可以帮助我们安排子项的垂直/水平位置,在默认的情况下,子项会以垂直方向上靠上(Arrangment.Top),水平方向上靠左(Alignment.Start)来排布
高度,那么就能使用 verticalArrangement 参数来定位子项在 Column 中的垂直位置。
宽度,那么就能使用 horizontalAlignment 参数来定位子项在 Column 中的水平位置。
大小,那么就可以同时使用以上的两个参数来定位子项的水平/垂直位置
alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Column {
Text(
text = "Hello, World!",
style = MaterialTheme.typography.h6
)
Text("Jetpack Compose")
}

Column(
modifier = Modifier
.border(1.dp, Color.Black)
) {
Text(
text = "Hello, World!",
style = MaterialTheme.typography.h6
)
Text("Jetpack Compose")
}

Column(
modifier = Modifier
.border(1.dp, Color.Black)
.size(150.dp),
verticalArrangement = Arrangement.Center
) {
Text(
text = "Hello, World!",
style = MaterialTheme.typography.h6,
modifier = Modifier.align(Alignment.CenterHorizontally)
//在给 Column 定义了大小之后,我们能够使用 Modifier.align 来单独设置子项的水平方向。
//设置了 Modifier.align 属性的子项会优先于 Column 的 horizontalAlignment 参数来定位。
)
Text("Jetpack Compose")
}

3.2 Row - 水平布局

Row 用于水平排列子组件(从左到右)。
事实上,Arrangment 就是来帮助我们快速安排好子项的位置,除了 Center(居中), Start(水平靠左), End(水平靠右)
alt text

1
2
3
4
5
6
7
@Composable
inline fun Row(
modifier: Modifier? = Modifier,
horizontalArrangement: Arrangement.Horizontal? = Arrangement.Start,
verticalAlignment: Alignment.Vertical? = Alignment.Top,
content: (@Composable @ExtensionFunctionType RowScope.() -> Unit)?
): Unit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 Surface(
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.padding(horizontal = 12.dp) // 设置 Surface 的外边距
.fillMaxWidth(),
elevation = 10.dp
) {
Column(
modifier = Modifier.padding(12.dp) // 里面内容的外边距
) {
Text(
text = "Jetpack Compose 是什么?",
style = MaterialTheme.typography.h6
)
Spacer(Modifier.padding(vertical = 5.dp))
Text(
text = "Jetpack Compose 是用于构建原生 Android 界面的新工具包。它可简化并加快 Android 上的界面开发,使用更少的代码、强大的工具和直观的 Kotlin API,快速让应用生动而精彩。"
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
IconButton(
onClick = { /*TODO*/ }
) {
Icon(Icons.Filled.Favorite, null)
}
IconButton(
onClick = { /*TODO*/ },
) {
Icon(painterResource(id = R.drawable.chat), null)
}
IconButton(
onClick = { /*TODO*/ },
) {
Icon(Icons.Filled.Share, null)
}
}
}
}

3.3 Box - 层叠布局

Box 用于层叠排列子组件,后面的组件会覆盖在前面的组件之上。

1
2
3
4
5
6
7
8
9
10
11
12
@Composable
Box {
Box(
modifier = Modifier.size(150.dp).background(Color.Green)
)
Box(
modifier = Modifier.size(80.dp).background(Color.Red)
)
Text(
text = "世界"
)
}

四、常用 UI 组件

4.1 Text 文本组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Composable
fun TextExample() {
Text(
text = "Hello Android",
color = Color.White, // 文字颜色
fontSize = 24.sp, // 字体大小
fontWeight = FontWeight.Bold, // 字体粗细
textAlign = TextAlign.Center, // 文字对齐
modifier = Modifier
.padding(16.dp)
.background(Color.Blue)
.fillMaxWidth()
)
}

4.2 Button 按钮组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Composable
fun ButtonExample() {
// 使用 remember 保存按钮状态
val buttonEnabled = remember { mutableStateOf(true) }

Button(
onClick = {
// 点击事件处理
buttonEnabled.value = false
println("按钮被点击了!")
},
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
enabled = buttonEnabled.value, // 按钮是否可用
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Green, // 背景颜色
contentColor = Color.White // 文字颜色
),
shape = RoundedCornerShape(8.dp), // 圆角形状
elevation = ButtonDefaults.elevation(
defaultElevation = 4.dp, // 默认阴影
pressedElevation = 8.dp // 按下时的阴影
)
) {
Text("点击我")
}
}

4.3 Image 图片组件

1
2
3
4
5
6
7
8
9
10
11
12
@Composable
fun ImageExample() {
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = "应用图标", // 无障碍描述(必填)
modifier = Modifier
.size(100.dp) // 图片尺寸
.clip(RoundedCornerShape(16.dp)) // 圆角裁剪
.border(2.dp, Color.Black), // 边框
contentScale = ContentScale.Crop // 图片缩放模式
)
}

4.4 Surface 表面组件

Surface 是一个带有材质设计效果的容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Composable
fun SurfaceExample() {
Surface(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
.height(100.dp),
elevation = 8.dp, // 阴影高度
shape = RoundedCornerShape(16.dp), // 圆角
color = MaterialTheme.colors.primary // 使用主题颜色
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.Center
) {
Text("Surface 示例", style = MaterialTheme.typography.h6)
Text("这是一个带有阴影的容器")
}
}
}

五、列表和滚动

5.1 LazyColumn - 垂直懒加载列表

LazyColumn 用于显示大量数据,只会渲染可见区域的内容,性能高效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Composable
fun LazyColumnExample() {
val items = List(100) { "Item $it" }

LazyColumn {
items(items) { item ->
Text(
text = item,
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
.background(Color.LightGray)
)
}
}
}

5.2 Spacer - 间距组件

Spacer 用于在布局中创建空白间距。

1
2
3
4
5
6
7
8
9
@Composable
fun SpacerExample() {
Column {
Text("上面的文本")
Spacer(modifier = Modifier.height(16.dp)) // 16dp 的垂直间距
Text("下面的文本")
Spacer(modifier = Modifier.width(32.dp)) // 32dp 的水平间距
}
}

六、状态管理

6.1 remember 和 mutableStateOf

在 Compose 中,使用 remembermutableStateOf 来管理组件的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Composable
fun CounterExample() {
// 使用 remember 记住计数器的值
val count = remember { mutableStateOf(0) }

Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("计数器: ${count.value}", fontSize = 24.sp)

Button(
onClick = { count.value++ } // 点击时计数器加1
) {
Text("增加")
}

Button(
onClick = { count.value = 0 } // 点击时重置
) {
Text("重置")
}
}
}

七、主题和样式

7.1 使用 MaterialTheme

MaterialTheme 提供了统一的主题配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Composable
fun MyApp() {
MaterialTheme {
// 整个应用使用 Material 主题
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
"标题",
style = MaterialTheme.typography.h4,
color = MaterialTheme.colors.primary
)
Text(
"正文",
style = MaterialTheme.typography.body1
)
}
}
}
}

八、完整示例

8.1 简单的待办事项应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@Composable
fun TodoApp() {
// 待办事项列表
val todos = remember { mutableStateListOf("学习 Compose", "写代码", "测试应用") }
// 新事项输入框文本
val newTodo = remember { mutableStateOf("") }

Column(
modifier = Modifier.padding(16.dp)
) {
// 标题
Text(
"待办事项",
style = MaterialTheme.typography.h4,
modifier = Modifier.padding(bottom = 16.dp)
)

// 输入框和添加按钮
Row {
TextField(
value = newTodo.value,
onValueChange = { newTodo.value = it },
placeholder = { Text("输入新事项") },
modifier = Modifier.weight(1f)
)

Button(
onClick = {
if (newTodo.value.isNotBlank()) {
todos.add(newTodo.value)
newTodo.value = ""
}
},
modifier = Modifier.padding(start = 8.dp)
) {
Text("添加")
}
}

Spacer(modifier = Modifier.height(16.dp))

// 待办事项列表
LazyColumn {
items(todos) { todo ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
elevation = 2.dp
) {
Row(
modifier = Modifier.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(todo)
IconButton(
onClick = { todos.remove(todo) }
) {
Icon(Icons.Default.Delete, "删除")
}
}
}
}
}
}
}

九、排列与对齐

9.1 按钮

9.2 文本输入框

9.3 图片

9.4 复选框

1
2
3
4
5
6
7
8
9
10
11
Checkbox(
checked = isChecked.value, // 绑定状态:是否选中
onCheckedChange = { isChecked.value = it }, // 状态更新回调
// 可选:自定义颜色
colors = CheckboxDefaults.colors(
checkedColor = androidx.compose.ui.graphics.Color.Blue, // 选中时颜色
uncheckedColor = androidx.compose.ui.graphics.Color.Gray, // 未选中边框色
checkmarkColor = androidx.compose.ui.graphics.Color.White // 对勾颜色
),
enabled = true // 是否禁用(false 则灰显,无法点击)
)

9.5 单选按钮

快捷键Shift+F6快速格式化

1
2
3
4
5
6
7
8
9
10
11
// 单选按钮
RadioButton(
selected = isSelected.value, // 绑定状态:是否选中
onClick = { isSelected.value = true }, // 点击时更新状态
// 可选:自定义颜色
colors = RadioButtonDefaults.colors(
selectedColor = androidx.compose.ui.graphics.Color.Blue, // 选中时颜色
unselectedColor = androidx.compose.ui.graphics.Color.Gray // 未选中颜色
),
enabled = true // 是否禁用(false 则灰显,无法点击)
)

9.6 开关

1
2
3
4
5
6
7
8
9
10
11
12
13
// 开关
Switch(
checked = isChecked.value, // 绑定状态:是否选中
onCheckedChange = { isChecked.value = it }, // 状态更新回调
// 可选:自定义颜色
colors = SwitchDefaults.colors(
checkedThumbColor = androidx.compose.ui.graphics.Color.Blue, // 选中时滑块颜色
uncheckedThumbColor = androidx.compose.ui.graphics.Color.Gray, // 未选中滑块颜色
checkedTrackColor = androidx.compose.ui.graphics.Color.LightGray, // 选中时轨道颜色
uncheckedTrackColor = androidx.compose.ui.graphics.Color.DarkGray // 未选中轨道颜色
),
enabled = true // 是否禁用(false 则灰显,无法点击)
)

9.7 下拉菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 下拉菜单
DropdownMenu(
expanded = isExpanded.value, // 绑定状态:是否展开
onDismissRequest = { isExpanded.value = false }, // 点击外部区域关闭
modifier = Modifier.padding(8.dp)
) {
DropdownMenuItem(
onClick = {
// 处理点击事件
isExpanded.value = false
}
) {
Text("选项1")
}
// 添加更多选项...
}
stringArrayResource(id = R.array.countryList)

9.8 Toast消息提示

1
2
// Toast消息提示
Toast.makeText(context, "这是一条 Toast 消息", Toast.LENGTH_SHORT).show()

9.9 Snackbar消息提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Snackbar消息提示
SnackbarHost(
hostState = snackbarHostState,
modifier = Modifier.padding(16.dp)
)
Scaffold(
topBar = {
TopAppBar(
title = { Text("待办事项") }
)
}
)
// 显示 Snackbar 消息
LaunchedEffect(key1 = Unit) {
snackbarHostState.showSnackbar("这是一条 Snackbar 消息")
}
SnackbarDuration.Indefinite
@OptIn(ExperimentalMaterial3Api::class)

9.10 对话框消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 对话框消息
AlertDialog(
onDismissRequest = { /* 处理取消 */ },
title = { Text("确认删除") },
text = { Text("确定要删除选中项吗?") },
confirmButton = {
Button(
onClick = { /* 处理确认 */ }
) {
Text("确认")
}
},
dismissButton = {
Button(
onClick = { /* 处理取消 */ }
) {
Text("取消")
}
}
)

按钮Button
// 按钮
Button(
onClick = { /* 处理点击 */ }
) {
Text("点击我")
}

IconButton(
onClick = { /* 处理点击 */ }
) {
Icon(Icons.Default.Delete, contentDescription = "删除")
}

9.11 顶部应用栏

1
2
3
4
// 顶部应用栏
TopAppBar(
title = { Text("待办事项") }
)

9.12 选项菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 选项菜单
DropdownMenu(
expanded = isExpanded.value, // 绑定状态:是否展开
onDismissRequest = { isExpanded.value = false }, // 点击外部区域关闭
modifier = Modifier.padding(8.dp)
) {
DropdownMenuItem(
onClick = {
// 处理点击事件
isExpanded.value = false
}
) {
Text("选项1")
}
// 添加更多选项...
}

9.13 导航四部分

1
2
3
4
5
6
7
8
9
// 导航四部分
NavigationBar {
NavigationBarItem(
selected = currentRoute == "home",
onClick = { navigateTo("home") },
icon = { Icon(Icons.Filled.Home, contentDescription = "首页") }
)
// 添加更多导航项...
}

9.14 列表

1
2
3
4
5
6
7
8
// 列表
LazyColumn {
items(items) { item ->
ListItem(
text = { Text(item) }
)
}
}

9.15 懒加载Column四部分

1
2
3
4
5
6
7
8
// 懒加载Column四部分
LazyColumn {
items(items) { item ->
ListItem(
text = { Text(item) }
)
}
}

9.16 懒加载Row

1
2
3
4
5
6
7
8
// 懒加载Row
LazyRow {
items(items) { item ->
ListItem(
text = { Text(item) }
)
}
}

9.17 懒加载Grid

1
2
3
4
5
6
7
8
9
10
11
// 懒加载Grid
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier.padding(8.dp)
) {
items(items) { item ->
ListItem(
text = { Text(item) }
)
}
}

9.18 待办事项列表五部分

1
2
3
4
5
6
7
8
// 待办事项列表五部分
LazyColumn {
items(items) { item ->
ListItem(
text = { Text(item) }
)
}
}

9.19 约束布局

1
2
3
4
// 约束布局
BoxWithConstraints {
// 子组件布局...
}

9.20 TextView文本视图

1
2
3
4
5
// TextView文本视图
Text(
text = "这是一条文本消息",
modifier = Modifier.padding(16.dp)
)

9.21 列表项imageView图片视图

1
2
3
4
5
6
7
8
9
10
// 列表项imageView图片视图
ListItem(
text = { Text("列表项") },
icon = {
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = "图标"
)
}
)

9.22 滚动视图

1
2
3
4
// 滚动视图
ScrollView {
// 子组件布局...
}

九、开发技巧

9.1 代码格式化

在 Android Studio 中,可以使用 Ctrl + Alt + L(Windows/Linux)或 Cmd + Option + L(Mac)来格式化代码。

9.2 实时预览

在函数上方添加 @Preview 注解,可以在 Android Studio 中实时预览 UI 效果:

1
2
3
4
5
@Preview(showBackground = true)
@Composable
fun PreviewExample() {
Text("这是预览文本")
}

9.3 调试技巧

  • 使用 println() 在 Logcat 中输出调试信息
  • 使用 Modifier.background(Color.Red) 临时添加背景色来查看组件边界
  • 使用 Android Studio 的 Layout Inspector 查看组件层次结构

十、学习路径建议

对于初学者,建议按照以下顺序学习:

  1. 基础概念:理解 @Composable、Modifier、状态管理
  2. 布局组件:掌握 Column、Row、Box 的使用
  3. 常用组件:学习 Text、Button、Image 等基础组件
  4. 列表和滚动:掌握 LazyColumn 的使用
  5. 主题和样式:学习 MaterialTheme 的应用
  6. 高级特性:学习动画、导航、架构等

总结

Jetpack Compose 是 Android 开发的未来趋势,它让 UI 开发变得更加简单、直观。通过本教程,你应该已经掌握了 Compose 的基础知识。继续练习和探索,你会发现 Compose 的强大之处!

记住:实践是最好的学习方式!