Jetpack Compose 常用开发总结

文档生成时间: 2026-04-07适用版本: Compose BOM 2024.02.00+ / Material 3


目录

  1. 概述
  2. 核心元素体系
  3. 基础布局组件
  4. 基础UI组件
  5. 输入控件
  6. 列表与网格
  7. Material Design 3 组件
  8. 状态管理
  9. 副作用与生命周期
  10. 性能优化
  11. 专家级技巧
  12. 最佳实践清单
  13. 常用依赖配置

概述

什么是 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 开发的未来。掌握其核心概念和最佳实践,能大幅提升开发效率和应用性能。

核心要点:

  1. 🎯 声明式思维:描述 UI 应该是什么样子
  2. 🔄 状态驱动:状态变化自动触发 UI 更新
  3. 性能优先:智能重组、按需更新
  4. 🧩 组件化:小组件组合成复杂界面
  5. 📦 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

总结

组件选择核心原则:

  1. 🎯 最小化原则:用最简单的组件达成目标
  2. 📦 组合优先:小组件组合 > 复杂大组件
  3. 性能考量:长列表用 Lazy,固定内容用普通组件
  4. 🎨 一致性:同一应用使用统一的组件风格
  5. 🔧 可测试性:组件职责单一,易于测试

架构决策要点:

  • 理解组件职责边界,选择最合适的组件
  • 关注性能影响,特别是列表和频繁更新场景
  • 保持代码可读性和可维护性
  • 遵循 Material Design 规范,保持视觉一致性
相关推荐
麦客奥德彪2 小时前
Jetpack Compose Modifier 完全指南
android
Mac的实验室4 小时前
(2026年最新)解决谷歌账号注册设备扫码短信发送失败无法验证难题(100%通过无需扫码验证)
android·google·程序员
半条咸鱼5 小时前
如何通过 ADB 连接安卓设备(USB + 无线 TCP/IP)
android
huwuhang5 小时前
斐讯盒子N1_YYFROM固件_webview119更新版附安卓专用遥控器刷机固工具USB_Burning_Tool
android
qq_352018685 小时前
android 状态栏高度获取
android
AirDroid_cn5 小时前
安卓15平板分屏比例能到1:9吗?极限分屏设置教程
android·智能手机
菜鸟国国6 小时前
还在为 Compose 屏幕适配发愁?一个 Density 搞定所有机型!
android
卡尔特斯6 小时前
Android Studio 代理配置指南
android·前端·android studio
sunbofiy236 小时前
去掉安卓的“读取已安装应用列表”,隐私合规
android