83、Jetpack Compose入门:Composable函数、布局、状态管理、Modifier

说实话,Compose 刚出来那会儿,我内心是抗拒的。毕竟 XML 写布局写了七八年,肌肉记忆都形成了。但真正上手写了两个页面后,我不得不承认——这玩意儿确实香。今天咱们就把 Compose 的四个核心概念掰开揉碎聊一聊。

一、Composable 函数:一切皆函数

在 Compose 里,UI 不再是 XML 文件,而是 Kotlin 函数。你写一个函数,加上 @Composable 注解,它就能生成 UI。听起来很玄乎?其实没那么复杂。

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

这个函数没有返回值,它直接描述 UI。我刚开始写的时候总想 return 一个 View,后来才明白——Composable 函数是“描述”UI,而不是“创建”UI。系统会在合适的时机调用它,生成或更新界面。

关键点:Composable 函数只能从另一个 Composable 函数中调用。你不能在普通 Kotlin 函数里直接调用它。

嗯,这里要注意:函数名建议用名词,比如 UserProfileProductCard,而不是 showUserProfile。这是 Compose 社区的约定,说白了就是把它当成 UI 组件来命名。

二、布局:Column、Row、Box 三板斧

XML 时代我们有 LinearLayout、RelativeLayout、FrameLayout。Compose 里对应的就是 Column、Row、Box。我个人习惯把它们叫做“布局三兄弟”。

Compose 布局 对应 XML 排列方向
Column LinearLayout(vertical) 垂直排列
Row LinearLayout(horizontal) 水平排列
Box FrameLayout 层叠排列
@Composable
fun SimpleLayout() {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("第一行")
        Text("第二行")
        Row {
            Text("左")
            Text("右")
        }
    }
}

我在项目中遇到过一个问题:Column 里嵌套 Row,Row 里的内容死活不居中。后来发现是没给 Row 设置 Modifier.fillMaxWidth()。你想想看,Row 默认只包裹内容,宽度不够自然没法居中。

小技巧:布局嵌套不要太深。超过三层嵌套,建议抽成独立的 Composable 函数。这样代码清晰,性能也好。

三、状态管理:remember 与 mutableStateOf

状态管理是 Compose 的灵魂。没有状态,UI 就是死的。Compose 的状态管理核心就两个东西:remembermutableStateOf

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

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

这段代码里,mutableStateOf(0) 创建了一个可观察的状态,remember 保证重组时状态不被重置。当 count 变化时,Compose 会自动重组用到这个状态的 UI 部分。

我曾经踩过一个坑:把状态定义在了 Composable 函数外面,结果页面旋转后状态丢了。后来才搞明白——remember 是跟 Composable 的生命周期绑定的。如果想让状态在配置变更后依然存活,得用 rememberSaveable

注意:不要在 Composable 函数里直接写 var count = 0。这样每次重组都会重置为 0,按钮永远显示 0 次。嗯,我刚开始就犯过这个错。

四、Modifier:链式调用的魔法

Modifier 是 Compose 里最灵活的部分。它用链式调用的方式,给组件添加布局、样式、事件等能力。说白了,它就是 Compose 版的 LayoutParams + 样式 + 监听器。

@Composable
fun StyledText() {
    Text(
        text = "Hello Compose",
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
            .background(Color.LightGray)
            .clickable { /* 点击事件 */ }
            .border(2.dp, Color.Blue)
    )
}

Modifier 的顺序很重要。你想想看,先 padding 再 background,和先 background 再 padding,效果完全不一样。前者是内边距后背景,后者是背景后内边距。我建议你记住一个原则:先布局,后样式,再事件

常用 Modifier 速查:

  • Modifier.fillMaxWidth() / fillMaxHeight() — 填充父容器
  • Modifier.padding() — 内边距
  • Modifier.background() — 背景色/背景图
  • Modifier.clickable() — 点击事件
  • Modifier.border() — 边框
  • Modifier.size() — 固定尺寸

五、知识体系总览

下面这张图把今天讲的内容串起来了。你可以看到,Composable 函数是基础,布局和 Modifier 负责 UI 呈现,状态管理负责数据驱动。三者缺一不可。

Jetpack Compose 核心概念 @Composable 函数 描述 UI 的 Kotlin 函数 布局 Column / Row / Box 状态管理 remember + mutableStateOf Modifier 链式调用配置 UI 数据驱动 状态变化 → UI 重组 Composable 函数 + 布局 + Modifier + 状态管理 = 完整 UI

六、实战:组合起来写个登录界面

光说不练假把式。咱们把上面四个概念组合起来,写一个简单的登录界面。

@Composable
fun LoginScreen() {
    var username by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }
    var isLoggedIn by remember { mutableStateOf(false) }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(24.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        if (isLoggedIn) {
            Text("登录成功!欢迎 $username")
        } else {
            OutlinedTextField(
                value = username,
                onValueChange = { username = it },
                label = { Text("用户名") },
                modifier = Modifier.fillMaxWidth()
            )
            Spacer(modifier = Modifier.height(12.dp))
            OutlinedTextField(
                value = password,
                onValueChange = { password = it },
                label = { Text("密码") },
                visualTransformation = PasswordVisualTransformation(),
                modifier = Modifier.fillMaxWidth()
            )
            Spacer(modifier = Modifier.height(24.dp))
            Button(
                onClick = { if (username.isNotBlank() && password.isNotBlank()) isLoggedIn = true },
                modifier = Modifier.fillMaxWidth()
            ) {
                Text("登录")
            }
        }
    }
}

这段代码里,我们用 remember 管理了三个状态,用 Column 做垂直布局,用 Modifier 设置了填充、内边距等样式。当用户点击登录按钮时,isLoggedIn 变为 true,UI 自动切换到登录成功页面。

个人建议:刚开始学 Compose,别急着用复杂的状态管理工具(比如 ViewModel + StateFlow)。先用 remembermutableStateOf 把基础搞明白。等遇到跨组件共享状态的需求时,再引入 ViewModel 也不迟。

好了,这一章的内容就到这里。Compose 的学习曲线其实比 XML 平缓,只要你理解了“函数描述 UI”这个核心思想,后面学起来会越来越顺。

公众号:蓝海资料掘金营,微信deep3321