Jetpack Compose 入门:声明式 UI 的安卓革命

2026.07.24· 约 1 分钟 ·tech·
#Android
#Jetpack Compose
#移动开发

为什么 Google 要再造一个 UI 框架?

Android 的传统 UI 方案(XML Layout + findViewById / ViewBinding + 命令式更新)有三个根本问题:

  1. 双语言开发 — XML 写布局,Kotlin/Java 写逻辑,来回切换心智负担重
  2. 状态同步地狱 — UI 是状态的投影,但你要手动 setText、setVisibility,极易不一致
  3. 耦合严重 — View 层级嵌套深、RecyclerView 的 Adapter 样板代码多得离谱

Jetpack Compose 的答案是声明式 UI:你描述 UI 应该长什么样,框架负责把它渲染出来并自动更新。

// 不需要 XML!一个函数就是一段 UI
@Composable
fun Greeting(name: String) {
    Text(
        text = "こんにちは、$name さん!",
        style = MaterialTheme.typography.headlineMedium
    )
}

@Composable 函数:新世界的积木

@Composable 注解告诉编译器:「这个函数要把数据转换成 UI」。它有以下特性:

  • 可以重排(recomposition) — 当依赖的状态变了,框架自动重新执行这个函数
  • 没有返回值 — 它向 Compose 运行时发射 UI 描述,不返回 UI 对象
  • 调用顺序不重要 — 可以在任何地方调用,甚至跨线程(预览时)
@Composable
fun NinjaCard(ninja: Ninja) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(text = ninja.name, style = MaterialTheme.typography.titleLarge)
            Spacer(modifier = Modifier.height(4.dp))
            Text(text = "${ninja.rank} · ${ninja.village}", color = MaterialTheme.colorScheme.secondary)
        }
    }
}

Modifier:Compose 的灵魂

如果说 @Composable 函数是骨架,那 Modifier 就是血肉。它控制组件的外观、布局、行为和交互:

@Composable
fun ModifierDemo() {
    Box(
        modifier = Modifier
            .size(100.dp)           // 尺寸
            .background(Color.Red)   // 背景
            .clip(CircleShape)       // 裁剪
            .border(2.dp, Color.White) // 边框
            .clickable { /* 点击 */ } // 交互
            .padding(8.dp)           // 内边距
    )
}

⚠️ Modifier 的顺序很重要! 链式调用是从上到下依次应用的。比如先 padding 再 background,背景不包括 padding 区域;反过来,先 background 再 padding,padding 区域也有背景色。

常用 Modifier 速查:

Modifier作用
size(width, height)固定尺寸
fillMaxWidth/Height/Size()撑满父容器
padding(start, top, ...)内边距
background(color, shape)背景色
clip(shape)裁剪形状
border(width, color, shape)边框
clickable { }点击事件
alpha(0.5f)透明度
shadow(elevation, shape)阴影

状态管理:remember 和 mutableStateOf

声明式 UI 的核心循环是:State 变化 → Recomposition → UI 更新。

@Composable
fun Counter() {
    // 记住跨 recomposition 的状态
    var count by remember { mutableStateOf(0) }

    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text(text = "カウント: $count", style = MaterialTheme.typography.headlineSmall)
        Spacer(modifier = Modifier.height(8.dp))
        Button(onClick = { count++ }) {
            Text("+1")
        }
    }
}

几个关键概念:

  • mutableStateOf — 创建可观察的状态对象。值改变时,所有读取它的 Composable 自动重组
  • remember — 在 recomposition 期间保留值。没有它,每次重组 count 都会重置为 0
  • by 委托 — 语法糖,相当于 count.value++,让代码更清爽

remember 的生命周期

@Composable
fun RememberLifecycle() {
    var count = remember { mutableStateOf(0) } // 组件离开组合树时被遗忘

    // 需要一个 key 来"记住版本"
    val user by remember(userId) { mutableStateOf(loadUser(userId)) }
    // key(userId) 改变时重新初始化
}

当 Composable 从组合树中被移除(比如导航到另一页),remember 存储的值就丢失了。要跨导航保留状态,可以用 rememberSaveable —— 它会在配置变更(旋转屏幕)后恢复。

布局系统:Column、Row、Box、LazyColumn

Compose 里没有 LinearLayout、RelativeLayout、FrameLayout。一切都简化为几个核心布局:

// Column = 垂直排列
@Composable
fun ColumnExample() {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,  // 主轴(垂直)对齐
        horizontalAlignment = Alignment.CenterHorizontally // 交叉轴对齐
    ) {
        Text("上")
        Spacer(modifier = Modifier.height(16.dp))
        Text("中")
        Spacer(modifier = Modifier.height(16.dp))
        Text("下")
    }
}

// Row = 水平排列
@Composable
fun RowExample() {
    Row(
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Icon(Icons.Default.Favorite, contentDescription = null)
        Text("居中")
        Button(onClick = {}) { Text("右对齐") }
    }
}

// Box = 层叠(类似 FrameLayout)
@Composable
fun BoxExample() {
    Box(contentAlignment = Alignment.Center) {
        Image(painter = painterResource(R.drawable.bg), contentDescription = null)
        Text("在图片上方")
    }
}

LazyColumn:高性能列表

@Composable
fun MissionList(missions: List<Mission>) {
    LazyColumn {
        items(missions, key = { it.id }) { mission ->
            MissionCard(mission)
        }
        // 列表为空时
        if (missions.isEmpty()) {
            item {
                Text("暂无任务", modifier = Modifier.padding(32.dp))
            }
        }
    }
}

LazyColumn 只组合屏幕可见的 item,滚出屏幕的 item 会被回收。要传 key,否则 item 错位是迟早的事。

MaterialTheme 与主题定制

// 定义颜色
private val DarkColors = darkColorScheme(
    primary = Color(0xFFFF6B1A),    // 火影橙
    secondary = Color(0xFFE8B830),  // 金色
    background = Color(0xFF0D0A08),
    surface = Color(0xFF181310)
)

// 定义主题
@Composable
fun NinjaTheme(content: @Composable () -> Unit) {
    MaterialTheme(
        colorScheme = DarkColors,
        typography = Typography, // 自定义字体
        content = content
    )
}

与 SwiftUI、Vue 的对比

特性Jetpack ComposeSwiftUIVue 3
语言KotlinSwiftTypeScript
声明方式@Composable funstruct View<template> + Composition API
状态管理mutableStateOf + remember@State / @StateObjectref / reactive
布局Column / Row / BoxVStack / HStack / ZStackCSS Flexbox / Grid
响应式模型重组(Recomposition)差异更新Proxy 依赖追踪

🎯 三大平台的声明式 UI 框架设计理念惊人地相似——都是状态驱动 UI 更新。学会一个,另外两个的思维模型可以直接迁移。

总结

Jetpack Compose 不是 XML Layout 的「语法糖版本」,它是安卓 UI 开发的范式转变。声明式 UI 让你用更少的代码实现更一致、更可预测的界面。如果你之前写惯了 XML + Adapter,最初几周会有些不适——特别是调试重组和性能优化——但一旦跨过那个坎,你就回不去了。

🔗相关忍术

00:00:00
系统已稳定运行:
Nuxt 3Vue 3Tailwind 3
粤ICP备2026111544号
🍥木葉忍村影分身之术!在代码、设计与写作间穿梭的技术忍者。这里记录着技...

© 2025-2026 木葉忍村 · Powered by Nuxt 3 & Tailwind CSS