跳到主要内容

Jetpack Compose

Jetpack Compose 是 Android 现代的声明式 UI 工具包,使用 Kotlin 代码构建界面,大幅简化了 UI 开发流程。本章节将介绍 Compose 的核心概念和常用组件。

什么是 Jetpack Compose?

Jetpack Compose 是 Android 官方推荐的现代 UI 开发方式,相比传统的 XML 布局,它有以下优势:

  • 声明式 UI:描述界面应该是什么样子,而不是如何构建
  • 简洁代码:减少样板代码,用更少的代码实现相同功能
  • 实时预览:在 Android Studio 中实时预览界面变化
  • 智能重组:只更新发生变化的部分,性能优异
  • 与现有代码兼容:可以与传统的 View 系统混合使用

添加依赖

在模块的 build.gradle.kts 中添加:

dependencies {
implementation("androidx.activity:activity-compose:1.8.2")
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material:material-icons-extended")

debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}

Composable 函数

Compose 使用 @Composable 注解标记可组合函数,这些函数描述界面的一部分。

基本示例

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

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
Greeting("Android")
}

在 Activity 中使用

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

布局组件

Column(垂直排列)

@Composable
fun ColumnExample() {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text("第一行")
Text("第二行")
Text("第三行")
}
}

Row(水平排列)

@Composable
fun RowExample() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("左边")
Text("右边")
}
}

Box(叠加布局)

@Composable
fun BoxExample() {
Box(
modifier = Modifier
.size(200.dp)
.background(Color.LightGray)
) {
Text(
"左上角",
modifier = Modifier.align(Alignment.TopStart)
)
Text(
"居中",
modifier = Modifier.align(Alignment.Center)
)
Text(
"右下角",
modifier = Modifier.align(Alignment.BottomEnd)
)
}
}

常用 Modifier

@Composable
fun ModifierExample() {
Column(
modifier = Modifier
.fillMaxSize() // 填满父容器
.padding(16.dp) // 内边距
.background(Color.White) // 背景色
.border(1.dp, Color.Gray, RoundedCornerShape(8.dp)) // 边框
.clickable { /* 点击事件 */ } // 点击
) {
Text(
"文本",
modifier = Modifier
.padding(8.dp)
.width(100.dp) // 宽度
.height(50.dp) // 高度
.clip(RoundedCornerShape(4.dp)) // 圆角裁剪
)
}
}

基础组件

Text(文本)

@Composable
fun TextExample() {
Column {
Text("普通文本")

Text(
"样式文本",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = Color.Blue,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)

Text(
buildAnnotatedString {
withStyle(style = SpanStyle(color = Color.Red)) {
append("红色")
}
append("普通")
withStyle(style = SpanStyle(fontSize = 24.sp)) {
append("大字")
}
}
)
}
}

Button(按钮)

@Composable
fun ButtonExample() {
Column {
Button(
onClick = { /* 点击事件 */ },
modifier = Modifier.padding(8.dp)
) {
Text("普通按钮")
}

OutlinedButton(onClick = { }) {
Text("边框按钮")
}

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

IconButton(onClick = { }) {
Icon(Icons.Default.Favorite, contentDescription = "收藏")
}

Button(
onClick = { },
enabled = false
) {
Text("禁用按钮")
}
}
}

Image(图片)

@Composable
fun ImageExample() {
Column {
// 资源图片
Image(
painter = painterResource(id = R.drawable.ic_launcher),
contentDescription = "应用图标",
modifier = Modifier.size(48.dp)
)

// 网络图片(需要 Coil 库)
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = "网络图片",
modifier = Modifier
.size(200.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
}
}

TextField(输入框)

@Composable
fun TextFieldExample() {
var text by remember { mutableStateOf("") }

Column {
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("用户名") },
placeholder = { Text("请输入用户名") },
singleLine = true,
modifier = Modifier.fillMaxWidth()
)

TextField(
value = text,
onValueChange = { text = it },
label = { Text("备注") },
modifier = Modifier
.fillMaxWidth()
.height(120.dp)
)
}
}

状态管理

remember 和 mutableStateOf

@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }

Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("计数: $count", style = MaterialTheme.typography.headlineMedium)

Row {
Button(onClick = { count-- }) {
Text("-")
}
Spacer(modifier = Modifier.width(16.dp))
Button(onClick = { count++ }) {
Text("+")
}
}
}
}

rememberSaveable

rememberSaveable 可以在配置变更(如屏幕旋转)后保留状态:

@Composable
fun PersistentCounter() {
var count by rememberSaveable { mutableStateOf(0) }

Button(onClick = { count++ }) {
Text("点击次数: $count")
}
}

ViewModel 集成

class CounterViewModel : ViewModel() {
private val _count = MutableStateFlow(0)
val count: StateFlow<Int> = _count

fun increment() {
_count.value++
}

fun decrement() {
_count.value--
}
}

@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
val count by viewModel.count.collectAsState()

Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("计数: $count")
Row {
Button(onClick = { viewModel.decrement() }) {
Text("-")
}
Button(onClick = { viewModel.increment() }) {
Text("+")
}
}
}
}

列表

LazyColumn

@Composable
fun UserList(users: List<User>) {
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(users) { user ->
UserItem(user)
}
}
}

@Composable
fun UserItem(user: User) {
Card(
modifier = Modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Default.Person,
contentDescription = null,
modifier = Modifier.size(48.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Column {
Text(user.name, style = MaterialTheme.typography.titleMedium)
Text(user.email, style = MaterialTheme.typography.bodyMedium)
}
}
}
}

LazyRow

@Composable
fun HorizontalList(items: List<String>) {
LazyRow(
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(items) { item ->
Card(
modifier = Modifier.size(120.dp)
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(item)
}
}
}
}
}

导航

添加依赖

dependencies {
implementation("androidx.navigation:navigation-compose:2.7.6")
}

基本导航

@Composable
fun NavExample() {
val navController = rememberNavController()

NavHost(navController = navController, startDestination = "home") {
composable("home") {
HomeScreen(
onNavigateToDetail = { id ->
navController.navigate("detail/$id")
}
)
}
composable(
"detail/{itemId}",
arguments = listOf(navArgument("itemId") { type = NavType.IntType })
) { backStackEntry ->
val itemId = backStackEntry.arguments?.getInt("itemId") ?: 0
DetailScreen(
itemId = itemId,
onBack = { navController.popBackStack() }
)
}
}
}

@Composable
fun HomeScreen(onNavigateToDetail: (Int) -> Unit) {
Column {
Text("首页")
Button(onClick = { onNavigateToDetail(123) }) {
Text("查看详情")
}
}
}

@Composable
fun DetailScreen(itemId: Int, onBack: () -> Unit) {
Column {
Text("详情页 ID: $itemId")
Button(onClick = onBack) {
Text("返回")
}
}
}

Material Design 3

Compose 默认使用 Material Design 3:

@Composable
fun MaterialThemeExample() {
MaterialTheme(
colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme(),
typography = Typography(),
shapes = Shapes()
) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column {
Text(
"标题",
style = MaterialTheme.typography.headlineLarge,
color = MaterialTheme.colorScheme.primary
)

Card(
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
Text("卡片内容")
}
}
}
}
}

小结

本章介绍了 Jetpack Compose 的核心内容:

  1. Composable 函数:声明式 UI 的基础
  2. 布局组件:Column、Row、Box 的使用
  3. 基础组件:Text、Button、Image、TextField
  4. 状态管理:remember、mutableStateOf、ViewModel 集成
  5. 列表:LazyColumn、LazyRow
  6. 导航:Navigation Compose
  7. Material Design 3:主题和样式

Jetpack Compose 是 Android UI 开发的未来方向,建议新项目优先使用 Compose。更多内容请参考 官方文档