首页 / Android 入门教程 / Compose 状态

Android 入门教程

Compose 状态

本教程共 100 篇 · 第 46 篇 · 更新于 2026-07-28 · 约 9 分钟阅读

AndroidAndroid 入门教程Jetpack Compose状态管理remember状态提升单向数据流

46. Compose 状态

本节目标:理解 Compose 中状态的概念,学会用 remember 和 mutableStateOf 管理状态,掌握状态提升原则和单向数据流,写出可交互的界面。

什么是状态

状态就是「会随时间变化的值」。用户在输入框里打字,文字就是状态;勾选了复选框,勾选状态就是状态;从网络加载了数据,数据就是状态。

在命令式 UI 里,状态存在控件内部(EditText.text),你手动读写。在 Compose 声明式 UI 里,状态和界面分离:数据变了,界面自动更新。但前提是—你得让 Compose 知道数据变了。

remember 记住值

可组合函数每次重组都会重新执行,函数里的局部变量会被重新初始化。要保留值,用 remember

@Composable
fun Counter() {
    // remember 让 count 在重组时保留
    val count = remember { 0 }
    Button(onClick = { /* 怎么改? */ }) {
        Text("点了 $count 次")
    }
}

但这里有个问题:countval,改不了。remember 只能记住值,不能让值可变。

mutableStateOf 可观察状态

mutableStateOf 创建一个可观察的状态容器。当它的值变化时,Compose 自动安排重组:

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("点了 $count 次")
    }
}

这里有三个关键点:

  1. mutableStateOf(0) 创建可观察状态,初始值 0。
  2. remember { } 把这个状态对象在重组间保留。
  3. by 委托语法让你直接读写 count,不用写 .value
Note

三种等价写法,选你喜欢的:

val count = remember { mutableStateOf(0) }  // 用 count.value 读写
var count by remember { mutableStateOf(0) }  // 用 count 直接读写(推荐)
val (count, setCount) = remember { mutableStateOf(0) }  // 解构

by 委托需要导入:

import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue

状态驱动的界面

Compose 的核心模式是:状态变化 -> 重组 -> 界面更新。你永远不直接改界面,而是改状态,让界面跟着状态走。

@Composable
fun TodoApp() {
    var text by remember { mutableStateOf("") }
    val items = remember { mutableStateListOf<String>() }

    Column {
        Row {
            TextField(
                value = text,
                onValueChange = { text = it },
                modifier = Modifier.weight(1f)
            )
            Button(onClick = {
                if (text.isNotBlank()) {
                    items.add(text)
                    text = ""
                }
            }) {
                Text("添加")
            }
        }
        items.forEach { item ->
            Text(item, modifier = Modifier.padding(8.dp))
        }
    }
}

mutableStateListOf 创建可观察的列表,增删时界面自动更新。

状态提升(State Hoisting)

状态写在哪很重要。如果一个可组合函数内部自己管理状态,它就是「有状态的」;如果状态由外部传入,它就是「无状态的」。

把状态从组件内部提到外部,叫状态提升。提升后的组件更通用、更可测试、更易复用。

有状态版本

@Composable
fun StatefulCounter() {
    var count by remember { mutableStateOf(0) }  // 状态在里面
    Button(onClick = { count++ }) {
        Text("点了 $count 次")
    }
}

无状态版本

@Composable
fun StatelessCounter(
    count: Int,               // 状态从外面传进来
    onIncrement: () -> Unit   // 事件交给外面处理
) {
    Button(onClick = onIncrement) {
        Text("点了 $count 次")
    }
}

调用方负责状态:

@Composable
fun CounterScreen() {
    var count by remember { mutableStateOf(0) }
    StatelessCounter(
        count = count,
        onIncrement = { count++ }
    )
}
Tip
  • 状态提升的原则:状态向下传递,事件向上传递。数据由父组件持有,子组件只负责展示和发出事件,不自己改状态。这叫单向数据流。

单向数据流

Compose 推荐的架构是单向数据流(Unidirectional Data Flow):

状态(State) -> 界面(UI) -> 事件(Event) -> 状态更新
  • 状态决定界面显示什么
  • 用户操作产生事件
  • 事件更新状态
  • 状态变化触发重组
@Composable
fun SearchScreen() {
    var query by remember { mutableStateOf("") }
    val results = remember(query) {
        if (query.isBlank()) emptyList()
        else allItems.filter { it.contains(query, ignoreCase = true) }
    }

    Column {
        SearchBar(
            query = query,
            onQueryChange = { query = it }  // 事件向上传
        )
        ResultsList(results)                // 状态向下传
    }
}
Note
  • remember(query) { } 带参数的版本:当 query 变化时才重新计算 results,不变时直接用缓存值。这避免每次重组都重新过滤列表。

rememberSaveable 保存状态

remember 在配置变更(旋转屏幕)时会丢失,因为 Activity 重建了。用 rememberSaveable 替代,它会把状态保存到 Bundle:

var count by rememberSaveable { mutableStateOf(0) }

旋转屏幕后 count 还在。它支持基本类型和实现了 Saver 的对象。

// 保存自定义对象
data class User(val name: String, val age: Int)

var user by rememberSaveable {
    mutableStateOf(User("小明", 20))
}
Warning
  • 不是所有对象都能自动保存。复杂对象需要实现自定义 Saver,或用 @Parcelize 注解让数据类支持 Parcelable。rememberSaveable 底层走的是 onSaveInstanceState

状态存储在哪

状态位置适用场景生命周期
组件内 remember简单 UI 状态组件存在期间
rememberSaveable需要挺过配置变更Activity 生命周期
ViewModel业务数据跨配置变更存活(第 78 章讲)
CompositionLocal主题、语言等全局值组合树生命周期
Tip
  • 经验法则:简单界面状态用 remember,要挺过旋转用 rememberSaveable,业务数据放 ViewModel。别把所有状态都塞 ViewModel,简单的 UI 状态在组件里管理就好。

小结

状态是 Compose 的驱动力:状态变 -> 重组 -> 界面更新。remember + mutableStateOf 管理可观察状态,rememberSaveable 让状态挺过配置变更。状态提升让组件更可复用:状态向下传,事件向上传,形成单向数据流。下一节学习文本和输入组件。