16、Compose基础:Compose思想、@Composable、Modifier、状态管理、重组、实战:声明式UI入门

各位同学,今天我们来聊聊Jetpack Compose。说实话,我第一次接触Compose时,内心是抗拒的。毕竟XML写了好多年,突然让我用Kotlin写UI,总觉得怪怪的。但真正用起来之后,我只想说一句——真香。

这一章,我会带你从零开始理解Compose的核心思想。不扯虚的,全是实战干货。

Compose思想:从命令式到声明式

传统的Android UI开发,是典型的命令式。你告诉系统:先创建TextView,再设置文字,然后添加到布局里。每一步都是指令。

Compose不一样。它是声明式的。你只需要描述UI应该长什么样,剩下的交给框架。

核心区别:

  • 命令式:如何做(How)
  • 声明式:做什么(What)

举个例子。你要显示一个用户名字。传统写法你得findViewById,然后setText。Compose呢?直接写一个Text组件,把数据传进去就行。数据变了,UI自动更新。

我个人习惯把Compose理解成「UI即函数」。输入数据,输出界面。没有中间商赚差价。

我的经验:刚开始学Compose时,别想着把老项目全改成Compose。先在一个小页面试试水。我当初就是在个人中心页面开始用的,改完发现代码量直接砍了一半。

@Composable:一切UI的起点

在Compose里,所有UI组件都是函数。但普通函数不行,得加上@Composable注解。

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

这个注解告诉编译器:嘿,这个函数要参与重组。说白了,Compose会跟踪这个函数的执行,当依赖的数据变化时,重新调用它。

你可能会问:那是不是所有UI都得加这个注解?嗯,基本上是的。但有个例外——如果你只是调用别的Composable函数,自己不需要直接操作UI,可以不加。不过实践中,我建议每个UI组件都加上,保持一致性。

注意:Composable函数不能直接写在普通函数里调用。必须在另一个Composable函数的作用域内。我曾经犯过这个错,编译直接报错,排查了半天。

Modifier:布局的瑞士军刀

Modifier是Compose里最灵活的概念。它相当于传统XML里的layout_xxx属性,但更强大。

你可以用Modifier控制大小、边距、点击事件、背景、动画……几乎所有UI属性。

@Composable
fun StyledButton() {
    Button(
        onClick = { /* 点击事件 */ },
        modifier = Modifier
            .padding(16.dp)
            .fillMaxWidth()
            .height(48.dp)
            .background(Color.Blue)
    ) {
        Text("点击我")
    }
}

Modifier的链式调用很优雅。每个方法返回一个新的Modifier,叠加效果。顺序很重要!先padding再background,和先background再padding,效果完全不同。

避坑指南:我曾经在项目里把Modifier的顺序搞反了,导致按钮的点击区域和视觉区域不一致。用户点空白区域也能触发点击,排查了好久才发现是Modifier顺序的问题。记住:先布局修饰,再视觉修饰。

状态管理:UI的灵魂

Compose的UI是数据的投影。数据变了,UI自动更新。这个数据就是状态。

最简单的状态管理用remembermutableStateOf

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

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

remember保证状态在重组时不被重置。mutableStateOf创建可观察的状态。当count变化时,Compose自动重组UI。

但注意,remember只在当前Composable的生命周期内有效。如果Activity重建,状态就丢了。想持久化?用rememberSaveable

状态提升:当多个组件需要共享状态时,把状态提到它们的公共父组件里。这是Compose的推荐做法。我见过不少新手把状态放在子组件里,结果兄弟组件之间传值传得乱七八糟。

重组:高效更新的秘密

重组是Compose最核心的机制。当状态变化时,Compose不会重新执行整个UI树,而是只更新受影响的部分。

怎么做到的?Compose会跟踪每个Composable函数读取了哪些状态。只有读取了变化状态的函数才会被重新调用。

举个例子:

@Composable
fun UserProfile(user: User) {
    Column {
        Text(user.name)  // 只依赖user.name
        Text(user.email) // 只依赖user.email
    }
}

如果只改了user.name,只有第一个Text会重组。第二个Text不受影响。

性能陷阱:别在Composable函数里做耗时操作。重组可能很频繁,每次重组都执行耗时操作,UI会卡顿。我见过有人直接在Composable里做网络请求,结果页面一刷新就卡死。记住:Composable只负责UI,数据逻辑放ViewModel里。

实战:声明式UI入门

光说不练假把式。我们来写一个简单的用户列表页面。

@Composable
fun UserListScreen() {
    val users = remember {
        listOf(
            User("张三", "zhangsan@example.com"),
            User("李四", "lisi@example.com"),
            User("王五", "wangwu@example.com")
        )
    }

    LazyColumn {
        items(users) { user ->
            UserCard(user)
        }
    }
}

@Composable
fun UserCard(user: User) {
    Card(
        modifier = Modifier
            .padding(8.dp)
            .fillMaxWidth()
    ) {
        Row(
            modifier = Modifier.padding(16.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            // 头像占位
            Box(
                modifier = Modifier
                    .size(48.dp)
                    .clip(CircleShape)
                    .background(Color.Gray)
            )
            Spacer(modifier = Modifier.width(12.dp))
            Column {
                Text(text = user.name, fontWeight = FontWeight.Bold)
                Text(text = user.email, color = Color.Gray)
            }
        }
    }
}

data class User(val name: String, val email: String)

这段代码实现了什么?一个可滚动的用户列表,每个卡片显示头像、姓名和邮箱。全部用声明式写法,没有XML,没有Adapter,没有ViewHolder。

你想想看,如果用传统方式,你得写一个RecyclerView,一个Adapter,一个ViewHolder,一个item布局文件。现在呢?一个Composable函数搞定。

我的建议:刚开始写Compose时,先模仿。照着官方示例敲一遍,理解每个组件的用法。然后试着改一改,比如把列表改成网格,或者加个搜索框。动手是最好的学习方式。

知识体系总览

下面这张图总结了本章的核心知识点。你可以把它当作学习地图。

Compose 核心 声明式思想 UI = f(state) 描述做什么,而非如何做 @Composable UI组件的函数标记 参与重组追踪 Modifier 布局、样式、事件 链式调用,顺序重要 状态管理 remember / mutableStateOf 状态提升 重组 智能局部更新 避免耗时操作 实战入门 LazyColumn + Card 声明式列表 核心:数据驱动UI,高效重组

这张图把Compose的核心概念串起来了。声明式思想是基础,@Composable是工具,Modifier是画笔,状态管理是数据源,重组是引擎。五者缺一不可。

好了,这一章的内容就到这里。Compose的世界很大,但入门其实不难。记住一句话:把UI当成函数的输出,数据变了,UI自然就变了。剩下的,就是多写多练。


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