文档生成时间: 2026-04-07适用版本: Compose BOM 2024.02.00+ / Material 3
目录
- 概述
- 核心元素体系
- 基础布局组件
- 基础UI组件
- 输入控件
- 列表与网格
- Material Design 3 组件
- 状态管理
- 副作用与生命周期
- 性能优化
- 专家级技巧
- 最佳实践清单
- 常用依赖配置
概述
什么是 Jetpack Compose?
Jetpack Compose 是 Android 推出的现代声明式 UI 工具包,采用 Kotlin DSL 编写 UI,告别传统的 XML 布局方式。
核心优势
| 特性 | 说明 |
|---|---|
| 声明式 UI | 描述 UI 应该是什么样子,而非如何构建 |
| 响应式编程 | 状态驱动 UI 更新,自动重组 |
| Kotlin ****DSL | 类型安全、IDE 友好的 UI 代码 |
| 互操作性 | 可与现有 View 体系混用 |
| Material 3 | 内置最新设计系统支持 |
与传统 View 对照
| 传统 View | Compose 对应 |
|---|---|
| LinearLayout (vertical) | Column |
| LinearLayout (horizontal) | Row |
| FrameLayout | Box |
| RecyclerView | LazyColumn / LazyRow |
| ConstraintLayout | ConstraintLayout (需依赖) |
核心元素体系
元素分类总览
css
┌─────────────────────────────────────────────────────────────┐
│ Jetpack Compose 元素体系 │
├─────────────────────────────────────────────────────────────┤
│ 布局组件 │ Column, Row, Box, ConstraintLayout │
├─────────────────────────────────────────────────────────────┤
│ 惰性布局 │ LazyColumn, LazyRow, LazyVerticalGrid │
├─────────────────────────────────────────────────────────────┤
│ 基础UI │ Text, Image, Icon, Button, TextField │
├─────────────────────────────────────────────────────────────┤
│ Material 3 │ TopAppBar, Card, Chip, FAB, NavigationBar │
├─────────────────────────────────────────────────────────────┤
│ 输入控件 │ TextField, Checkbox, Switch, RadioButton │
├─────────────────────────────────────────────────────────────┤
│ 容器组件 │ Scaffold, Surface, Card, BadgedBox │
├─────────────────────────────────────────────────────────────┤
│ 动画组件 │ AnimatedVisibility, Crossfade │
├─────────────────────────────────────────────────────────────┤
│ 状态管理 │ remember, mutableStateOf, derivedStateOf │
├─────────────────────────────────────────────────────────────┤
│ 副作用API │ LaunchedEffect, SideEffect, DisposableEffect│
└─────────────────────────────────────────────────────────────┘
基础布局组件
1. Column - 垂直布局
对应传统 View: LinearLayout (vertical)
适用场景:
- 表单页面
- 列表项内容
- 垂直排列的内容块
scss
@Composable
fun ColumnExample() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp), // 子项间距
horizontalAlignment = Alignment.CenterHorizontally // 水平居中
) {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
}
核心参数:
| 参数 | 类型 | 说明 |
|---|---|---|
modifier |
Modifier | 修饰符 |
verticalArrangement |
Arrangement | 垂直排列方式 |
horizontalAlignment |
Alignment | 水平对齐方式 |
Arrangement 选项:
Arrangement.Top- 顶部对齐(默认)Arrangement.Center- 居中Arrangement.Bottom- 底部对齐Arrangement.SpaceBetween- 两端对齐Arrangement.SpaceEvenly- 均匀分布Arrangement.spacedBy(8.dp)- 固定间距
2. Row - 水平布局
对应传统 View: LinearLayout (horizontal)
适用场景:
- 工具栏
- 水平按钮组
- 标签行
kotlin
@Composable
fun RowExample() {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("Left")
Text("Center")
Text("Right")
}
}
3. Box - 堆叠布局
对应传统 View: FrameLayout
适用场景:
- 图标叠加
- 浮动按钮
- 背景层叠
kotlin
@Composable
fun BoxExample() {
Box(
modifier = Modifier.size(100.dp),
contentAlignment = Alignment.Center
) {
// 底层
Image(
painter = painterResource(R.drawable.background),
contentDescription = null,
modifier = Modifier.fillMaxSize()
)
// 顶层
Text("Overlay")
}
}
4. ConstraintLayout - 约束布局
需要依赖: androidx.constraintlayout:constraintlayout-compose:1.0.1
适用场景:
- 复杂布局
- 需要精确约束关系
- 减少嵌套层级
scss
@Composable
fun ConstraintLayoutExample() {
ConstraintLayout(modifier = Modifier.fillMaxSize()) {
val (title, subtitle, button) = createRefs()
Text(
text = "Title",
modifier = Modifier.constrainAs(title) {
top.linkTo(parent.top, margin = 16.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
Text(
text = "Subtitle",
modifier = Modifier.constrainAs(subtitle) {
top.linkTo(title.bottom, margin = 8.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
Button(
onClick = { },
modifier = Modifier.constrainAs(button) {
bottom.linkTo(parent.bottom, margin = 16.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
) {
Text("Action")
}
}
}
基础UI组件
1. Text - 文本
scss
@Composable
fun TextExamples() {
Column {
// 基础文本
Text("Hello Compose")
// 样式文本
Text(
text = "Styled Text",
style = MaterialTheme.typography.headlineLarge,
color = MaterialTheme.colorScheme.primary,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
// 富文本
Text(buildAnnotatedString {
withStyle(style = SpanStyle(color = Color.Red)) {
append("Red ")
}
withStyle(style = SpanStyle(color = Color.Blue, fontWeight = FontWeight.Bold)) {
append("Blue Bold")
}
})
}
}
2. Icon - 图标
kotlin
@Composable
fun IconExamples() {
Row {
// Material 图标
Icon(
imageVector = Icons.Default.Home,
contentDescription = "Home",
tint = Color.Blue,
modifier = Modifier.size(24.dp)
)
// 填充图标
Icon(Icons.Filled.Favorite, "Favorite")
// 轮廓图标
Icon(Icons.Outlined.Star, "Star")
// 圆角图标
Icon(Icons.Rounded.Person, "Person")
}
}
3. Image - 图片
ini
@Composable
fun ImageExamples() {
// 资源图片
Image(
painter = painterResource(R.drawable.image),
contentDescription = "Description",
modifier = Modifier
.size(100.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
// 网络图片(需要 Coil 库)
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = null,
modifier = Modifier.clip(RoundedCornerShape(8.dp)),
placeholder = painterResource(R.drawable.placeholder),
error = painterResource(R.drawable.error)
)
}
4. 按钮系列
scss
@Composable
fun ButtonExamples() {
Column {
// 主要按钮
Button(onClick = { }) {
Icon(Icons.Default.Check, contentDescription = null)
Spacer(Modifier.width(8.dp))
Text("Confirm")
}
// 轮廓按钮
OutlinedButton(onClick = { }) {
Text("Cancel")
}
// 文字按钮
TextButton(onClick = { }) {
Text("Skip")
}
// 图标按钮
IconButton(onClick = { }) {
Icon(Icons.Default.Settings, "Settings")
}
// 浮动操作按钮
FloatingActionButton(onClick = { }) {
Icon(Icons.Default.Add, "Add")
}
// 扩展 FAB
ExtendedFloatingActionButton(
onClick = { },
icon = { Icon(Icons.Default.Edit, null) },
text = { Text("Compose") }
)
}
}
按钮选择指南:
| 场景 | 推荐组件 |
|---|---|
| 主要操作(提交、确认) | Button |
| 次要操作(取消) | OutlinedButton |
| 低优先级操作(跳过) | TextButton |
| 工具栏操作 | IconButton |
| 页面主要动作 | FloatingActionButton |
输入控件
1. TextField - 文本输入
ini
@Composable
fun TextFieldExamples() {
var text by remember { mutableStateOf("") }
// 基础文本框
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Username") },
placeholder = { Text("Enter username") },
leadingIcon = { Icon(Icons.Default.Person, null) },
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
// 轮廓文本框(Material 3 推荐)
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("Password") },
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
isError = text.length < 6,
supportingText = { Text("至少6个字符") }
)
}
2. Checkbox / Switch / RadioButton
scss
@Composable
fun InputExamples() {
// 复选框
var checked by remember { mutableStateOf(true) }
Row(verticalAlignment = Alignment.CenterVertically) {
Checkbox(checked = checked, onCheckedChange = { checked = it })
Text("Agree to terms")
}
// 开关
var enabled by remember { mutableStateOf(false) }
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Dark mode")
Switch(checked = enabled, onCheckedChange = { enabled = it })
}
// 单选按钮组
var selected by remember { mutableStateOf(0) }
Row {
listOf("Option 1", "Option 2", "Option 3").forEachIndexed { index, label ->
Row(verticalAlignment = Alignment.CenterVertically) {
RadioButton(
selected = selected == index,
onClick = { selected = index }
)
Text(label)
}
}
}
}
3. Slider - 滑块
kotlin
@Composable
fun SliderExample() {
var position by remember { mutableStateOf(50f) }
Column {
Text("Volume: ${position.toInt()}")
Slider(
value = position,
onValueChange = { position = it },
valueRange = 0f..100f,
steps = 9 // 10 档
)
}
}
列表与网格
1. LazyColumn - 垂直列表
对应传统 View: RecyclerView(垂直)
适用场景:
- 长列表
- 数据动态加载
- 需要滚动性能优化
scss
@Composable
fun LazyColumnExample() {
val items = remember { listOf("A", "B", "C", "D", "E") }
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(16.dp)
) {
// Header
item {
Text("Header", style = MaterialTheme.typography.titleLarge)
}
// 列表项
items(items) { item ->
ListItem(item)
}
// 带索引的列表项
itemsIndexed(items) { index, item ->
Text("Item $index: $item")
}
// Footer
item {
Text("Footer")
}
}
}
⚠️ 性能关键:使用 key 参数
scss
// ❌ 错误:不使用 key
items(items) { item ->
ItemView(item)
}
// ✅ 正确:使用稳定 key
items(items, key = { it.id }) { item ->
ItemView(item)
}
2. LazyRow - 水平列表
scss
@Composable
fun LazyRowExample() {
LazyRow(
horizontalArrangement = Arrangement.spacedBy(12.dp),
contentPadding = PaddingValues(horizontal = 16.dp)
) {
items(categories) { category ->
CategoryChip(category)
}
}
}
3. LazyVerticalGrid - 网格
scss
@Composable
fun GridExample() {
LazyVerticalGrid(
columns = GridCells.Fixed(2), // 固定2列
// 或 GridCells.Adaptive(100.dp) 自适应列宽
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(16.dp)
) {
items(photos) { photo ->
PhotoItem(photo)
}
}
}
4. 粘性标题
less
@Composable
fun StickyHeaderExample() {
LazyColumn {
stickyHeader {
Text(
"Section A",
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceVariant)
.padding(16.dp)
)
}
items(sectionAItems) { /* ... */ }
stickyHeader {
Text("Section B", modifier = Modifier.background(Color.Gray))
}
items(sectionBItems) { /* ... */ }
}
}
Material Design 3 组件
1. Scaffold - 页面脚手架
适用场景: 完整页面布局
ini
@Composable
fun ScaffoldExample() {
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
topBar = {
TopAppBar(
title = { Text("My App") },
navigationIcon = {
IconButton(onClick = { }) {
Icon(Icons.Default.ArrowBack, "Back")
}
},
actions = {
IconButton(onClick = { }) {
Icon(Icons.Default.Share, "Share")
}
}
)
},
bottomBar = {
NavigationBar {
items.forEach { item ->
NavigationBarItem(
icon = { Icon(item.icon, null) },
label = { Text(item.label) },
selected = selectedItem == item,
onClick = { selectedItem = item }
)
}
}
},
floatingActionButton = {
FloatingActionButton(onClick = { }) {
Icon(Icons.Default.Add, "Add")
}
},
snackbarHost = { SnackbarHost(snackbarHostState) }
) { paddingValues ->
// ⚠️ 必须使用 paddingValues 避免内容被遮挡
Column(modifier = Modifier.padding(paddingValues)) {
// 页面内容
}
}
}
2. 卡片组件
scss
@Composable
fun CardExamples() {
// 标准卡片
Card(
modifier = Modifier.padding(16.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
shape = RoundedCornerShape(12.dp)
) {
Text("Card content")
}
// 凸起卡片
ElevatedCard {
Text("Elevated card")
}
// 轮廓卡片
OutlinedCard {
Text("Outlined card")
}
}
3. Chip 系列
ini
@Composable
fun ChipExamples() {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
// 辅助 Chip
AssistChip(
onClick = { },
label = { Text("Assist") },
leadingIcon = { Icon(Icons.Default.Call, null) }
)
// 筛选 Chip
var selected by remember { mutableStateOf(false) }
FilterChip(
selected = selected,
onClick = { selected = !selected },
label = { Text("Filter") },
leadingIcon = if (selected) {
{ Icon(Icons.Default.Check, null) }
} else null
)
// 输入 Chip
InputChip(
selected = false,
onClick = { },
label = { Text("Tag") },
trailingIcon = { Icon(Icons.Default.Close, null) }
)
}
}
4. 对话框
ini
@Composable
fun DialogExample() {
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
title = { Text("Confirm Delete") },
text = { Text("Are you sure you want to delete this item?") },
confirmButton = {
TextButton(onClick = {
// 执行删除
showDialog = false
}) {
Text("Delete")
}
},
dismissButton = {
TextButton(onClick = { showDialog = false }) {
Text("Cancel")
}
},
icon = { Icon(Icons.Default.Warning, null) }
)
}
}
5. 底部表单
kotlin
@Composable
fun BottomSheetExample() {
var showSheet by remember { mutableStateOf(false) }
val sheetState = rememberModalBottomSheetState()
if (showSheet) {
ModalBottomSheet(
onDismissRequest = { showSheet = false },
sheetState = sheetState,
dragHandle = { BottomSheetDefaults.DragHandle() }
) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Sheet Title", style = MaterialTheme.typography.titleLarge)
// Sheet content
}
}
}
}
状态管理
核心概念
Compose 的 UI 是状态驱动的:状态变化 → 触发重组 → UI 更新
1. remember - 缓存计算结果
kotlin
@Composable
fun RememberExample() {
// 缓存计算结果,重组时不会重新计算
val expensiveResult = remember {
// 仅在首次组合时执行
heavyComputation()
}
// 缓存对象
val formatter = remember { SimpleDateFormat("yyyy-MM-dd") }
}
2. mutableStateOf - 可观察状态
kotlin
@Composable
fun StateExample() {
// 方式 1:委托属性(推荐)
var count by remember { mutableStateOf(0) }
// 方式 2:直接使用
val state = remember { mutableStateOf(0) }
// state.value = 1
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
3. derivedStateOf - 派生状态
作用: 避免不必要的重组,仅在依赖变化时重新计算
kotlin
@Composable
fun DerivedStateExample() {
val listState = rememberLazyListState()
// ❌ 错误:每次滚动都会重组
val showButton = listState.firstVisibleItemScrollOffset > 100
// ✅ 正确:使用 derivedStateOf
val showButton by remember {
derivedStateOf {
listState.firstVisibleItemScrollOffset > 100
}
}
Box {
LazyColumn(state = listState) { /* ... */ }
if (showButton) {
ScrollToTopButton()
}
}
}
4. rememberSaveable - 配置变更保存
kotlin
@Composable
fun SaveableExample() {
// 屏幕旋转后状态会保留
var text by rememberSaveable { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it }
)
}
副作用与生命周期
什么是副作用?
在 Composable 函数中执行的非 UI 渲染操作,如网络请求、数据库访问、订阅等。
1. LaunchedEffect - 启动协程
kotlin
@Composable
fun LaunchedEffectExample(userId: String) {
var user by remember { mutableStateOf<User?>(null) }
// 当 userId 变化时重新执行
LaunchedEffect(userId) {
user = loadUser(userId)
}
user?.let { UserView(it) }
}
2. rememberCoroutineScope - 获取协程作用域
kotlin
@Composable
fun CoroutineScopeExample() {
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Button(onClick = {
// 在非 Composable 上下文中启动协程
scope.launch {
snackbarHostState.showSnackbar("Saved!")
}
}) {
Text("Save")
}
}
3. DisposableEffect - 资源清理
kotlin
@Composable
fun DisposableEffectExample() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
// 处理生命周期事件
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
// 清理资源
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
4. SideEffect - 同步到非 Compose 代码
kotlin
@Composable
fun SideEffectExample(count: Int) {
// 每次成功重组后执行
SideEffect {
// 更新外部状态、埋点等
analytics.logEvent("count_changed", count)
}
}
副作用 API 选择指南
| API | 用途 | 特点 |
|---|---|---|
| LaunchedEffect | 执行挂起函数 | 有 key,可取消 |
| rememberCoroutineScope | 在事件处理中启动协程 | 返回 CoroutineScope |
| DisposableEffect | 需要清理的资源 | 必须有 onDispose |
| SideEffect | 同步到非 Compose 代码 | 每次重组执行 |
性能优化
1. 重组机制理解
重组(Recomposition): 当状态变化时,Compose 重新执行依赖该状态的 Composable 函数。
智能重组原则:
- 仅重组受影响的部分
- 跳过未变化的 Composable
- 可组合函数应该是无副作用的
2. 避免不必要重组
使用 remember 缓存
kotlin
// ❌ 错误:每次重组都创建新对象
@Composable
fun BadExample(data: List<Item>) {
val sortedData = data.sortedBy { it.name } // 每次都排序
// ...
}
// ✅ 正确:使用 remember
@Composable
fun GoodExample(data: List<Item>) {
val sortedData = remember(data) { data.sortedBy { it.name } }
// ...
}
使用 derivedStateOf 控制重组粒度
kotlin
// ❌ 错误:高频状态变化导致频繁重组
val showButton = listState.firstVisibleItemIndex > 10
// ✅ 正确:派生状态,仅当条件变化时重组
val showButton by remember {
derivedStateOf { listState.firstVisibleItemIndex > 10 }
}
使用 key 参数
scss
// ❌ 错误:无 key,列表变化时全部重组
LazyColumn {
items(items) { item ->
ItemView(item)
}
}
// ✅ 正确:有 key,仅重组变化的项
LazyColumn {
items(items, key = { it.id }) { item ->
ItemView(item)
}
}
3. 稳定类型
什么是稳定类型?
- 所有 public 属性不可变,或都是稳定类型
- 属性变化时 Compose 能感知
标记稳定类型:
less
@Immutable
data class User(
val id: String,
val name: String
)
@Stable
data class UIState(
val loading: Boolean = false,
val data: List<Item> = emptyList()
)
4. 避免过度嵌套
sql
// ❌ 错误:过度嵌套
Column {
Row {
Column {
Row {
Text("Too nested")
}
}
}
}
// ✅ 正确:扁平化布局
Column {
Text("Flat")
}
性能优化清单
| 优化项 | 做法 |
|---|---|
| 缓存计算 | 使用 remember |
| 控制重组 | 使用 derivedStateOf |
| 列表优化 | 使用 key 参数 |
| 避免嵌套 | 扁平化布局 |
| 稳定类型 | 使用 @Immutable / @Stable |
| 跳过重组 | 合理使用 Modifier |
专家级技巧
1. Modifier 顺序的重要性
⚠️ Modifier 顺序会影响效果
scss
// 顺序 1:先 padding 再背景
Modifier
.padding(16.dp) // 外边距
.background(Color.Blue) // 背景色不包含 padding 区域
.padding(16.dp) // 内边距
// 顺序 2:先背景再 padding
Modifier
.background(Color.Blue) // 背景色填满整个区域
.padding(16.dp) // 内容有 padding
2. 使用 CompositionLocal 传递数据
kotlin
// 定义
val LocalUser = compositionLocalOf { User() }
// 提供
@Composable
fun App() {
CompositionLocalProvider(LocalUser provides currentUser) {
// 子组件可通过 LocalUser.current 访问
UserProfile()
}
}
// 使用
@Composable
fun UserProfile() {
val user = LocalUser.current
Text(user.name)
}
3. 自定义 Modifier
ini
fun Modifier.shimmer(): Modifier = composed {
var size by remember { mutableStateOf(IntSize.Zero) }
val transition = rememberInfiniteTransition(label = "shimmer")
val startOffsetX by transition.animateFloat(
initialValue = -2 * size.width.toFloat(),
targetValue = 2 * size.width.toFloat(),
animationSpec = infiniteRepeatable(animation = tween(1000)),
label = "shimmer"
)
background(
brush = Brush.linearGradient(
colors = listOf(Color.LightGray, Color.White, Color.LightGray),
start = Offset(startOffsetX, 0f),
end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat())
)
)
.onGloballyPositioned { size = it.size }
}
4. 使用 produceState
kotlin
@Composable
fun loadUser(userId: String): State<Result<User>> = produceState(
initialValue = Result.loading(),
key1 = userId
) {
value = Result.success(repository.getUser(userId))
}
5. 使用 rememberUpdatedState
kotlin
@Composable
fun TimerExample(onTimeout: () -> Unit) {
// 确保使用最新的回调
val currentOnTimeout by rememberUpdatedState(onTimeout)
LaunchedEffect(Unit) {
delay(5000)
currentOnTimeout() // 使用最新引用
}
}
最佳实践清单
✅ 必须做
| 实践 | 说明 |
|---|---|
| 使用 ViewModel 管理 UI 状态 | 状态提升、生命周期感知 |
| 为 Composable 添加 @Preview | 快速预览、调试 |
| 使用 remember 缓存计算结果 | 避免重复计算 |
| LazyColumn/LazyRow 使用 key | 提升列表性能 |
| Scaffold 使用 paddingValues | 避免内容被遮挡 |
| Composable 函数保持幂等 | 无副作用 |
❌ 避免做
| 反模式 | 后果 |
|---|---|
| 过度嵌套布局 | 性能下降 |
| 在 Composable 中创建对象(不使用 remember) | 重复创建 |
| 忽略 key 参数 | 列表性能差 |
| 直接在 Composable 中执行 I/O | 阻塞 UI |
| Mutable 参数 | 状态管理混乱 |
常用依赖配置
scss
dependencies {
// BOM 管理版本
val composeBom = platform("androidx.compose:compose-bom:2024.02.00")
implementation(composeBom)
androidTestImplementation(composeBom)
// 核心组件
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
debugImplementation("androidx.compose.ui:ui-tooling")
// Material 3
implementation("androidx.compose.material3:material3")
// Material 2(兼容)
implementation("androidx.compose.material:material")
// 图标扩展
implementation("androidx.compose.material:material-icons-extended")
// 约束布局
implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1")
// 视图模型
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
// 导航
implementation("androidx.navigation:navigation-compose:2.7.7")
// 图片加载 (Coil)
implementation("io.coil-kt:coil-compose:2.5.0")
}
总结
Jetpack Compose 是 Android 现代化 UI 开发的未来。掌握其核心概念和最佳实践,能大幅提升开发效率和应用性能。
核心要点:
- 🎯 声明式思维:描述 UI 应该是什么样子
- 🔄 状态驱动:状态变化自动触发 UI 更新
- ⚡ 性能优先:智能重组、按需更新
- 🧩 组件化:小组件组合成复杂界面
- 📦 Material 3:内置最新设计系统
持续实践,从简单页面开始,逐步掌握高级技巧。
参考资源:
补充:容器与表面组件
Surface - 表面容器
定位: Material Design 的基础容器组件,用于设置背景、形状、阴影等视觉属性。
适用场景:
- 需要设置背景色、阴影的容器
- 需要点击效果的区域
- 卡片、按钮等的基础
ini
@Composable
fun SurfaceExamples() {
// 基础 Surface
Surface(
modifier = Modifier
.fillMaxWidth()
.height(100.dp),
color = MaterialTheme.colorScheme.surface,
contentColor = MaterialTheme.colorScheme.onSurface,
tonalElevation = 2.dp, // 色调海拔(影响背景色)
shadowElevation = 4.dp, // 阴影海拔
shape = RoundedCornerShape(12.dp),
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outline),
onClick = { /* 点击处理 */ }
) {
// 内容
Text("Surface content")
}
// 可点击 Surface
Surface(
onClick = { /* 处理点击 */ },
modifier = Modifier.padding(16.dp),
shape = MaterialTheme.shapes.medium,
color = MaterialTheme.colorScheme.primaryContainer
) {
Text(
"Clickable Surface",
modifier = Modifier.padding(16.dp)
)
}
// 带选中状态的 Surface
var selected by remember { mutableStateOf(false) }
Surface(
selected = selected,
onClick = { selected = !selected },
modifier = Modifier.size(80.dp),
shape = CircleShape,
color = if (selected)
MaterialTheme.colorScheme.primary
else
MaterialTheme.colorScheme.surfaceVariant
) {
Box(contentAlignment = Alignment.Center) {
Icon(
Icons.Default.Check,
contentDescription = null,
tint = if (selected) Color.White else Color.Gray
)
}
}
}
Surface vs Card 选择指南:
| 特性 | Surface | Card |
|---|---|---|
| 定位 | 基础容器 | 专用卡片容器 |
| 点击支持 | ✅ 原生支持 | ✅ 通过 Modifier |
| 选中状态 | ✅ 支持 | ❌ 不支持 |
| 默认样式 | 最小样式 | Material 卡片样式 |
| 适用场景 | 按钮、容器、可点击区域 | 内容卡片、信息展示 |
Card 深度用法
ini
@Composable
fun AdvancedCardExamples() {
// Elevated Card - 凸起效果
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
elevation = CardDefaults.elevatedCardElevation(
defaultElevation = 6.dp,
pressedElevation = 12.dp,
focusedElevation = 8.dp,
hoveredElevation = 8.dp
),
colors = CardDefaults.elevatedCardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
),
onClick = { /* 点击处理 */ }
) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Elevated Card", style = MaterialTheme.typography.titleLarge)
Text("This card has elevated shadow effect")
}
}
// Outlined Card - 轮廓边框
OutlinedCard(
modifier = Modifier.fillMaxWidth(),
border = BorderStroke(
width = 1.dp,
color = MaterialTheme.colorScheme.outline
),
shape = MaterialTheme.shapes.large
) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Default.Info,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
Spacer(Modifier.width(12.dp))
Text("Outlined Card with border")
}
}
// 可展开 Card
var expanded by remember { mutableStateOf(false) }
Card(
modifier = Modifier.fillMaxWidth(),
onClick = { expanded = !expanded }
) {
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("Expandable Card")
Icon(
if (expanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
contentDescription = null
)
}
AnimatedVisibility(visible = expanded) {
Text(
"Hidden content revealed!",
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
)
}
}
}
}
BadgedBox - 徽章容器
scss
@Composable
fun BadgeExamples() {
// 数字徽章
BadgedBox(
badge = {
Badge { Text("99+") }
},
modifier = Modifier.padding(16.dp)
) {
Icon(
Icons.Default.Notifications,
contentDescription = "Notifications",
modifier = Modifier.size(28.dp)
)
}
// 状态点徽章
BadgedBox(
badge = {
Badge(
containerColor = Color.Green,
modifier = Modifier.size(8.dp)
)
}
) {
Icon(Icons.Default.Person, contentDescription = null)
}
// 带动画的徽章
var count by remember { mutableStateOf(0) }
BadgedBox(
badge = {
AnimatedVisibility(visible = count > 0) {
Badge { Text("$count") }
}
}
) {
IconButton(onClick = { count++ }) {
Icon(Icons.Default.ShoppingCart, "Cart")
}
}
}
Divider - 分割线
scss
@Composable
fun DividerExamples() {
Column {
Text("Section 1")
// 水平分割线
HorizontalDivider(
modifier = Modifier.padding(vertical = 8.dp),
thickness = 1.dp,
color = MaterialTheme.colorScheme.outlineVariant
)
Text("Section 2")
// 带缩进的分割线
HorizontalDivider(
modifier = Modifier.padding(start = 16.dp),
thickness = 2.dp,
color = MaterialTheme.colorScheme.primary
)
Row(modifier = Modifier.height(50.dp)) {
Text("Left")
// 垂直分割线
VerticalDivider(
modifier = Modifier.padding(horizontal = 8.dp),
thickness = 1.dp,
color = MaterialTheme.colorScheme.outline
)
Text("Right")
}
}
}
Spacer - 间距占位
scss
@Composable
fun SpacerExamples() {
Column {
Text("Header")
// 固定间距
Spacer(Modifier.height(16.dp))
Text("Content")
// 弹性间距(占据剩余空间)
Spacer(Modifier.weight(1f))
Text("Footer")
// 水平间距
Row {
Text("Left")
Spacer(Modifier.width(8.dp))
Text("Right")
}
}
}
补充:专家视角架构决策指南
决策树:选择正确的组件
scss
需要容器组件?
├─ 需要卡片效果?
│ ├─ 需要 Material 样式 → Card / ElevatedCard / OutlinedCard
│ └─ 需要最小样式 → Surface
│
├─ 需要点击区域?
│ ├─ 带背景/阴影 → Surface(onClick)
│ └─ 纯点击 → Modifier.clickable / Modifier.toggleable
│
├─ 需要徽章?
│ └─ BadgedBox
│
└─ 需要分隔?
├─ 水平 → HorizontalDivider
└─ 垂直 → VerticalDivider
决策树:选择正确的布局
sql
布局类型?
│
├─ 垂直排列
│ ├─ 固定内容 → Column
│ ├─ 长列表 → LazyColumn
│ └─ 需要粘性头部 → LazyColumn + stickyHeader
│
├─ 水平排列
│ ├─ 固定内容 → Row
│ └─ 横向滚动 → LazyRow
│
├─ 堆叠布局
│ ├─ 简单堆叠 → Box
│ └─ 精确定位 → ConstraintLayout
│
├─ 网格布局
│ ├─ 固定列数 → LazyVerticalGrid + GridCells.Fixed
│ └─ 自适应列宽 → LazyVerticalGrid + GridCells.Adaptive
│
└─ 完整页面
└─ Scaffold
组件搭配最佳实践
1. 列表项布局
scss
// ✅ 推荐:ListItem + Card 组合
@Composable
fun UserListItem(user: User, onClick: () -> Unit) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 4.dp),
onClick = onClick
) {
Row(
modifier = Modifier.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// 头像
Surface(
modifier = Modifier.size(48.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer
) {
// AsyncImage 或 Icon
}
Spacer(Modifier.width(12.dp))
// 文字信息
Column(modifier = Modifier.weight(1f)) {
Text(user.name, style = MaterialTheme.typography.titleMedium)
Text(user.email, style = MaterialTheme.typography.bodySmall)
}
// 状态徽章
if (user.isOnline) {
Badge(containerColor = Color.Green)
}
}
}
}
2. 表单页面布局
ini
@Composable
fun FormScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// 表单分组
Surface(
modifier = Modifier.fillMaxWidth(),
shape = MaterialTheme.shapes.medium,
tonalElevation = 1.dp
) {
Column(modifier = Modifier.padding(16.dp)) {
Text("个人信息", style = MaterialTheme.typography.titleMedium)
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("姓名") },
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = email,
onValueChange = { email = it },
label = { Text("邮箱") },
modifier = Modifier.fillMaxWidth()
)
}
}
// 另一个分组
Surface(
modifier = Modifier.fillMaxWidth(),
shape = MaterialTheme.shapes.medium,
tonalElevation = 1.dp
) {
// ...
}
// 提交按钮
Spacer(Modifier.weight(1f))
Button(
onClick = { /* 提交 */ },
modifier = Modifier.fillMaxWidth()
) {
Text("提交")
}
}
}
3. Dashboard 布局
ini
@Composable
fun DashboardScreen() {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Dashboard") },
actions = {
BadgedBox(
badge = { Badge { Text("3") } }
) {
IconButton(onClick = { }) {
Icon(Icons.Default.Notifications, null)
}
}
}
)
}
) { paddingValues ->
LazyVerticalGrid(
columns = GridCells.Adaptive(150.dp),
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
// 统计卡片
items(statItems) { item ->
StatCard(item)
}
}
}
}
@Composable
fun StatCard(item: StatItem) {
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f),
colors = CardDefaults.elevatedCardColors(
containerColor = item.color
)
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.SpaceBetween
) {
Icon(
item.icon,
contentDescription = null,
modifier = Modifier.size(32.dp)
)
Column {
Text(
item.value,
style = MaterialTheme.typography.headlineMedium
)
Text(
item.label,
style = MaterialTheme.typography.bodySmall
)
}
}
}
}
性能优化决策矩阵
| 场景 | 推荐方案 | 避免 |
|---|---|---|
| 长列表 | LazyColumn + key | Column + modifier.verticalScroll |
| 频繁更新 | derivedStateOf | 直接读取 state |
| 复杂计算 | remember | 每次重组计算 |
| 动画 | AnimatedVisibility | 手动条件渲染 |
| 列表滚动状态 | rememberLazyListState + derivedStateOf | 直接读取 firstVisibleItemIndex |
组件选型速查表
| 需求 | 首选 | 备选 |
|---|---|---|
| 垂直布局 | Column | LazyColumn(长列表) |
| 水平布局 | Row | LazyRow(横向滚动) |
| 堆叠布局 | Box | ConstraintLayout(复杂约束) |
| 卡片容器 | Card | Surface(更简洁) |
| 可点击区域 | Surface | Modifier.clickable |
| 列表项 | LazyColumn items | Column(少量固定项) |
| 网格 | LazyVerticalGrid | Row + Column(固定网格) |
| 输入框 | OutlinedTextField | TextField |
| 按钮 | Button | OutlinedButton / TextButton |
| 图标按钮 | IconButton | FAB(主要操作) |
| 分隔 | HorizontalDivider | Spacer(无视觉分隔) |
| 徽章 | BadgedBox | 自定义 Box + Badge |
总结
组件选择核心原则:
- 🎯 最小化原则:用最简单的组件达成目标
- 📦 组合优先:小组件组合 > 复杂大组件
- ⚡ 性能考量:长列表用 Lazy,固定内容用普通组件
- 🎨 一致性:同一应用使用统一的组件风格
- 🔧 可测试性:组件职责单一,易于测试
架构决策要点:
- 理解组件职责边界,选择最合适的组件
- 关注性能影响,特别是列表和频繁更新场景
- 保持代码可读性和可维护性
- 遵循 Material Design 规范,保持视觉一致性