本文将学习如何在 Android 应用中使用 Jetpack Compose 管理 State
。
在前面两篇文章里我们已经创建了一个包含列表名称和按钮的简单布局。在每个列表项中添加按钮后,我们会进一步添加点击交互。
当用户点击按钮时,列表项会展开(expanded),同时按钮的文本将从“See More”变为“See Less”。
在此过程中,我们需要两个变量:extraPadding
和 expanded
。extraPadding
用于控制列表项的宽度,expanded
则用于标识列表项的状态,即是否展开。
@Composable
fun Greeting(name: String) {
var expanded = false
val extraPadding = if (expanded) 48.dp else 8.dp
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(
modifier = Modifier.padding(24.dp)
) {
Column(
modifier = Modifier
.weight(1f)
.padding(bottom = extraPadding)
) {
Text(text = "Hello, ")
Text(text = name)
}
ElevatedButton(
onClick = { expanded = !expanded }
) {
Text(text = if (expanded) "Show Less" else "Show More")
}
}
}
}
此外,还需更改 MyList()
函数的调用:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeAppTheme {
MyList(names)
}
}
}
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
ComposeAppTheme {
MyList(names)
}
}
运行应用,注意到当点击按钮时,UI 没有任何变化。原因是 State
没有被 Compose 读取,因为 expanded
变量是普通的布尔值。
管理 State
为了让 Compose 能够检测 expanded
的变化,我们需要使用 mutableStateOf()
来定义 expanded
变量。
var expanded = mutableStateOf(false)
还需要为 Greeting()
Composable 添加注解:
@SuppressLint("UnrememberedMutableState")
@Composable
fun Greeting(name: String) {
// 代码暂时没有变化
}
并且修改代码中的如下部分:
val extraPadding = if (expanded.value) 48.dp else 8.dp
同样修改按钮点击操作中的 expanded
变量:
ElevatedButton(
onClick = { expanded.value = !expanded.value }
) {
Text(text = if (expanded.value) "Show Less" else "Show More")
}
再次运行应用,尽管点击按钮仍未触发 UI 变化,但 expanded
的 State
已被 Compose 读取。
Recomposition(重构)
Recomposition
是指当某个 State
发生变化时,Compose 会重新调用 Composable 函数来触发 UI 更新。
在上面的代码中,虽然 State
已经被读取并发生了变化,但 expanded
的值并未保存在内存中,因此每次重构后,它又恢复为 false
。
为了解决这个问题,我们可以使用 remember
将 expanded
的值保存在内存中:
var expanded = remember { mutableStateOf(false) }
去掉 SuppressLint
注解,代码如下:
@Composable
fun Greeting(name: String) {
// 代码暂时没有变化
}
再次运行应用,expanded
的值现在已经存储在内存中,因此 UI 能够根据状态变化正常更新。
使用 Kotlin 委托属性简化代码
为了减少样板代码(boilerplate)并让代码更加简洁,我们可以使用 Kotlin 的委托属性(delegated properties):
var expanded by remember { mutableStateOf(false) }
最终的 Greeting()
Composable 函数代码如下:
@Composable
fun Greeting(name: String) {
var expanded by remember { mutableStateOf(false) }
val extraPadding = if (expanded) 48.dp else 8.dp
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(
modifier = Modifier.padding(24.dp)
) {
Column(
modifier = Modifier
.weight(1f)
.padding(bottom = extraPadding)
) {
Text(text = "Hello, ")
Text(text = name)
}
ElevatedButton(
onClick = { expanded = !expanded }
) {
Text(text = if (expanded) "Show Less" else "Show More")
}
}
}
}
至此,应用的 State
管理和 UI 响应已经实现。