Skip to content

Jetpack Compose 最佳实践

本文档基于 Jetpack Compose BOM 2026.04.01(Compose 1.8),说明 Compose 的最佳实践、推荐特性、避坑指南和性能优化建议。

说明:本文档为通用 Compose 最佳实践,所有示例使用领域无关的类名和场景。项目特定的 IME 架构模式(MVI 状态管理、IMEService 桥接、键盘渲染等)属于架构设计范畴,不在技能文档中描述。


1. Compose 适用性评估

✅ 高度适用的场景

场景Compose 方案优势
设置页面标准 Compose 组件声明式 UI、主题统一、代码简洁
列表 / 网格LazyRow/LazyColumn/LazyVerticalGrid替代自定义 RecyclerView
标签 / 分类ScrollableTabRow + HorizontalPager标准化分页
主题切换isSystemInDarkTheme()自动跟随系统主题
按键反馈扩展的触觉反馈 API丰富的按键触觉反馈类型

⚠️ 需谨慎评估的场景

场景风险建议
高频重绘区域重组开销需控制使用 rememberderivedStateOfkey 精确控制重组范围
自定义 Canvas 绘制与 Compose Canvas 的桥接使用 Compose Canvas + drawBehind/drawWithContent
自定义手势检测需要极低延迟pointerInput + awaitPointerEventScope,注意帧率
Service 中的 ComposeView生命周期与普通 Activity 不同注意内存管理和 Composition 策略

❌ 不适用 Compose 的场景

场景原因替代方案
极端性能要求的自定义绘制Compose 重组和布局有固有的帧预算开销降级为传统 View + Canvas
需要像素级控制的自定义布局Compose 布局系统有固有约束自定义 Layout 或降级为传统 View

2. Compose 1.8 新特性应用

2.1 文本自动缩放(TextAutoSize)— ★★★★★ 关键

在需要适应不同宽度和屏幕尺寸的文本场景中使用:

kotlin
BasicText(
    text = item.label,
    maxLines = 1,
    autoSize = TextAutoSize.StepBased(
        minFontSize = 10.sp,
        maxFontSize = 18.sp,
        stepSize = 1.sp,
    ),
)

// 带中间省略
BasicText(
    text = item.description,
    maxLines = 1,
    autoSize = TextAutoSize.StepBased(
        minFontSize = 12.sp,
        maxFontSize = 16.sp,
        stepSize = 1.sp,
    ),
    overflow = TextOverflow.MiddleEllipsis,
)

2.2 智能省略号 — ★★★★★ 关键

kotlin
// 中间省略,保留首尾
Text(
    text = longText,
    overflow = TextOverflow.MiddleEllipsis,
    maxLines = 1,
)

// 开头省略,保留尾部
Text(
    text = filePath,
    overflow = TextOverflow.StartEllipsis,
    maxLines = 1,
)

2.3 扩展触觉反馈 — ★★★★★ 关键

kotlin
val haptics = LocalHapticFeedback.current

// 点击反馈
haptics.performHapticFeedback(HapticFeedbackType.ContextClick)

// 长按触发
haptics.performHapticFeedback(HapticFeedbackType.LongPress)

// 确认操作
haptics.performHapticFeedback(HapticFeedbackType.Confirm)

// 手势结束
haptics.performHapticFeedback(HapticFeedbackType.GestureEnd)

2.4 预测性动画(Predictive Animations)— ★★★★☆ 重要

尺寸变化动画(如展开 / 折叠):

kotlin
LookaheadScope {
    Box(
        Modifier
            .width(if (expanded) 180.dp else 110.dp)
            .animateBounds(lookaheadScope = this@LookaheadScope)
    ) {
        // 内容
    }
}

2.5 稳定的多焦点 API — ★★★★★ 关键

在需要管理多个焦点区域的界面中使用:

kotlin
Column(Modifier.focusRestorer()) {
    SectionA(Modifier.onFocusChanged { /* ... */ })
    SectionB(Modifier.onFocusChanged { /* ... */ })
    Toolbar(Modifier.onFocusChanged { /* ... */ })
}

2.6 可见性追踪 — ★★★★☆ 重要

优化分组列表的懒加载:

kotlin
LazyColumn {
    items(groups) { group ->
        GroupView(
            group = group,
            modifier = Modifier.onLayRectChanged { rect ->
                group.isVisible = rect.isVisible
            },
        )
    }
}

2.7 Autofill 语义 — ★★★★☆ 重要

kotlin
TextField(
    state = rememberTextFieldState(),
    modifier = Modifier.semantics { contentType = ContentType.Username },
    label = { Text("用户名") },
)

2.8 可暂停组合 — ★★★★☆ 重要

Compose 1.8 自动启用,在复杂布局切换场景中自动分发重组工作到多帧,防止卡顿。

2.9 Lazy Layout 优化 — ★★★★☆ 重要

Compose 1.8 自动应用更智能的预取策略,优化大列表和网格的滚动性能。

2.10 HTML AnnotatedString — ★★★☆☆ 有用

kotlin
val htmlContent = buildAnnotatedString {
    appendHtml(htmlSource)
}
Text(text = htmlContent)

3. 性能优化规范

3.1 重组控制

kotlin
// ✅ 推荐:使用 remember 和 derivedStateOf 减少重组
@Composable
fun ItemList(items: List<Item>) {
    val visibleItems = remember(items) {
        items.take(MAX_VISIBLE)
    }

    LazyRow {
        items(visibleItems, key = { it.id }) { item ->
            ItemView(item)
        }
    }
}

// ❌ 避免:每次重组都做计算
@Composable
fun ItemList(items: List<Item>) {
    LazyRow {
        items(items.take(MAX_VISIBLE)) { item -> // 每次重组都创建新列表
            ItemView(item)
        }
    }
}

3.2 列表项的稳定性

kotlin
// ✅ 推荐:使用 key 标识稳定的列表项
LazyVerticalGrid(
    columns = GridCells.Fixed(columnCount),
) {
    items(items = items, key = { it.stableId }) { item ->
        ItemView(item)
    }
}

3.3 避免不必要的重组

kotlin
// ✅ 推荐:Lambda 不捕获频繁变化的 state
GesturePanel(
    onGesture = { gesture -> viewModel.handleGesture(gesture) },
)

// ❌ 避免:Lambda 捕获频繁变化的 state
GesturePanel(
    onGesture = { gesture ->
        val current = state.value // 每次调用都读取 state
        process(gesture, current)
    },
)

4. 手势处理

4.1 点击 / 长按手势

kotlin
// ✅ 推荐:使用 pointerInput 处理自定义手势
Modifier.pointerInput(key) {
    awaitEachGesture {
        val down = awaitFirstDown(requireUnconsumed = false)
        // 按下反馈
        onPressed()

        val up = waitForUpOrCancellation()
        if (up == null) {
            // 取消/移出
            onCancelled()
        } else {
            // 抬起
            onReleased()
        }
    }
}

4.2 滑行 / 拖拽手势

kotlin
Modifier.pointerInput(Unit) {
    awaitEachGesture {
        val down = awaitFirstDown()
        val startPosition = down.position

        val path = mutableListOf(startPosition)
        do {
            val event = awaitPointerEvent()
            path.addAll(event.changes.map { it.position })
        } while (event.changes.any { it.pressed })

        onSwipePath(startPosition, path)
    }
}

5. ComposeView 内存管理

在 Service 等非标准生命周期组件中使用 ComposeView 需要特别注意内存管理:

kotlin
class MyService : Service() {
    private var composeView: ComposeView? = null

    override fun onCreate(): View {
        return ComposeView(this).also { composeView = it }.apply {
            // ✅ 关键:设置正确的 Composition 策略
            setViewCompositionStrategy(
                ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
            )
            setContent {
                AppTheme {
                    Content()
                }
            }
        }
    }

    override fun onDestroy() {
        // ✅ 关键:清理 ComposeView 引用,避免内存泄漏
        composeView = null
        super.onDestroy()
    }
}

注意:如果 Service 在后台持有 ComposeView 引用,可能导致内存泄漏。确保在 onDestroy() 中清理引用。