Compose 中的状态
应用的“状态”是指可以随时间变化的任何值。这是一个非常宽泛的定义,从 Room 数据库到类的变量,全部涵盖在内。
所有 Android 应用都会向用户显示状态。下面是 Android 应用中的一些状态示例:
- 聊天应用中最新收到的消息。
- 用户的个人资料照片。
- 在项列表中的滚动位置。
关键提示:状态决定界面在任何特定时间的显示内容。
下面的示例通过构建一个健康App应用来展示如何正确的使用Compose中的状态。
构建的第一项功能是饮水计数器,用于记录您一天饮用了多少杯水。
创建一个名为 WaterCounter 的可组合函数,其中包含一个 Text 可组合项,用于显示饮水杯数。饮水杯数应存储在名为 count 的值中。
创建一个包含 WaterCounter 可组合函数的新文件 WaterCounter.kt,如下所示:
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
Column(modifier = modifier.padding(16.dp)) {
val count = 0
Text(
text = "You've had $count glasses.",
modifier = modifier.padding(16.dp)
)
Button(onClick = {
count++ }, Modifier.padding(top = 8.dp)) {
Text("Add one")
}
}
}
创建一个代表主屏幕的文件 WellnessScreen.kt,然后调用 WaterCounter 函数:
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun WellnessScreen(modifier: Modifier = Modifier) {
WaterCounter(modifier)
}
打开 MainActivity.kt,在 activity 的 setContent 块内调用新创建的 WellnessScreen 可组合项,如下所示:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BasicStateCodelabTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
WellnessScreen()
}
}
}
}
}
提示:setContent 内部的 MainActivity 中使用的应用主题取决于项目名称。此处假定项目名为 BasicStateCodelab。
如果现在运行应用,您会看到我们的基本饮水计数器屏幕,其中包含硬编码的饮水杯数。

WaterCounter 可组合函数的状态为变量 count。但是,点击按钮时,您会发现没有任何反应。为 count 变量设置不同的值不会使 Compose 将其检测为状态更改,因此不会产生任何效果。这是因为,当状态发生变化时,您尚未指示 Compose 应重新绘制屏幕(即“重组”可组合函数)。
可组合函数中的记忆功能
Compose 应用通过调用可组合函数将数据转换为界面。组合是指 Compose 在执行可组合项时构建的界面描述。如果发生状态更改,Compose 会使用新状态重新执行受影响的可组合函数,从而创建更新后的界面。这一过程称为重组。Compose 还会查看各个可组合项需要哪些数据,以便仅重组数据发生了变化的组件,而避免重组未受影响的组件。
- 组合:Jetpack Compose 在执行可组合项时构建的界面描述。
- 初始组合:通过首次运行可组合项创建组合。
- 重组:在数据发生变化时重新运行可组合项以更新组合。
为此,Compose 需要知道要跟踪的状态,以便在收到更新时安排重组。
Compose 采用特殊的状态跟踪系统,可以为读取特定状态的任何可组合项安排重组。 这让Compose 能够实现精细控制,并且仅重组需要更改的可组合函数,而不是重组整个界面。这将通过同时跟踪针对状态的“写入”(即状态变化)和针对状态的“读取”来实现。
使用 Compose 的 State
和 MutableState
类型让 Compose 能够观察到状态。
Compose 会跟踪每个读取状态 value 属性的可组合项,并在其 value 更改时触发重组。您可以使用 mutableStateOf 函数来创建可观察的 MutableState。它接受初始值作为封装在 State 对象中的参数,这样便可使其 value 变为可观察。
现在修改代码如下:
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
Column(modifier = modifier.padding(16.dp)) {
// Changes to count are now tracked by Compose
val count: MutableState<Int> = mutableStateOf(0)
Text("You've had ${
count.value} glasses.")
Button(onClick = {
count.value++ }, Modifier.padding(top = 8.dp)) {
Text("Add one")
}
}
}
如前所述,对 count 所做的任何更改都会安排对自动重组读取 count 的 value 的所有可组合函数进行重组。在此情况下,点击按钮即会触发重组 WaterCounter。
如果现在运行应用,您会再次发现没有发生任何变化!
安排重组的过程没有问题。不过,当重组发生时,变量 count 会重新初始化为 0,因此我们需要通过某种方式在重组后保留此值。
为此,我们可以使用 remember 可组合内嵌函数。系统会在初始组合期间将由 remember 计算的值存储在组合中,并在重组期间一直保持存储的值。
现在继续修改代码如下:
import androidx.compose.runtime.remember
@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
Column(modifier = modifier.padding(16.dp)) {
val count: MutableState<Int> = remember {
mutableStateOf(0) }
Text("You've had ${
count.value} glasses.")
Button(onClick = {
count.value++ }, Modifier.padding(top = 8.dp)) {
Text("Add one")
}
}
}
remember 和 mutableStateOf 通常在可组合函数中一起使用。您可以将 remember 视为一种在组合中存储单个对象的机制,就像私有 val 属性在对象中执行的操作一样。
这里还可以使用 Kotlin 的委托属性来简化 count 的使用,通过关键字 by 将 count 定义为 var。通过添加委托的 getter 和 setter 导入内容,我们可以间接读取 count 并将其设置为可变,而无需每次都显式引用 MutableState 的 value 属性。
修改代码如下:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
Column(modifier = modifier.padding(16.dp)) {
var count by remember {
mutableStateOf(0) }
Text("You've had $count glasses.")
Button(onClick = {
count++ }, Modifier.padding(top = 8.dp)) {
Text("Add one")
}
}
}
现在运行应用,您的计数器已就绪且可正常运行!

这种安排可与用户形成数据流反馈循环:
- 界面向用户显示状态(当前计数显示为文本)。
- 用户生成的事件会与现有状态合并以生成新状态(点击按钮会为当前计数加一)
状态驱动型界面
Compose 是一个声明性界面框架。它描述界面在特定状况下的状态,而不是在状态发生变化时移除界面组件或更改其可见性。调用重组并更新界面后,可组合项最终可能会进入或退出组合。

此方法可避免像针对视图系统那样手动更新视图的复杂性。这也不太容易出错,因为您不会忘记根据新状态更新视图,因为系统会自动执行此过程。
如果在初始组合期间或重组期间调用了可组合函数,则认为其存在于组合中。未调用的可组合函数(例如,由于该函数在 if 语句内调用且未满足条件)不存在于组合中。
关键提示:如果界面是相对用户而言的,那么界面状态就是相对应用而言的。这就像同一枚硬币的两面,界面是界面状态的直观呈现。对界面状态所做的任何更改都会立即反映在界面中。
组合的输出是描述界面的树结构。
Android Studio 的布局检查器工具可用于检查 Compose 生成的应用布局。我们接下来将执行此操作。
为了演示此过程,请修改代码,以根据状态显示界面。打开 WaterCounter,如果 count 大于 0,则显示 Text:
@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
Column(modifier = modifier.padding(16.dp)) {
var count by remember {
mutableStateOf(0) }
if (count > 0) {
// This text is present if the button has been clicked
// at least once; absent otherwise
Text("You've had $count glasses.")
}
Button(onClick = {
count++ }, Modifier.padding(top =