Jetpack Compose 的状态
众所周知一个正常的变量在 Android 视图中发生更新时是不会触发任何事件的, 需要想各种方法(LiveData, Databinding, Observe, ViewModel) 等各种方法让它在更新时重绘视图以及不同情况下保存变量值
为此,Compose 需要知道要跟踪的状态,以便在收到更新时安排重组
Compose 采用特殊的状态跟踪系统,可以为读取特定状态的任何可组合项安排重组。这让 Compose 能够实现精细控制,并且仅重组需要更改的可组合函数,而不是重组整个界面。这将通过同时跟踪针对状态的“写入”(即状态变化)和针对状态的“读取”来实现
在组件函数中保持值更新
需要使用 State
和 MutableState
类型来让 Compose 知晓其状态:
kotlin
@Composable
fun WaterCounter(modifier: Modifier = Modifier){
// 使用 by 进行委托
var count by mutableStateOf(0)
Column (modifier = Modifier.padding(16.dp)) {
Text(
text = "You've had $count glasses.",
)
Button(onClick = { count++ }, Modifier.padding(top = 8.dp)) {
Text("Add")
}
}
}
但是这还不够, 首先这个可组合函数是一个函数, 而在每次 mutableStateOf
内的值更新时会引发该组件重组, 因此会导致其 count
的值重新赋值为0, 从而导致没有效果
所以我们还需要引入一个 remember
来让初始组合时的 count
存储起来并一直保持不被重组重赋
kotlin
var count by remember {
mutableStateOf(0)
}
现在它们能工作了
而你有时候可能会遭遇例如:亮暗色主题切换、语言切换或横竖切换
此时 Activity 会被重组, 此时需要用到 rememberSaveable
, 不然当 Activity 被重组时,状态依旧会被刷新清除掉
状态控制组件
@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 = 8.dp)) {
Text("Add one")
}
}
}
在 count
大于 0 时 Text 会显示!
状态驱动界面在给定时刻显示哪些元素
界面的不同部分可以依赖于相同的状态, 修改 Button
, 使其在 count
达到 10 之前处于启用状态,并在达到 10 之后停用(即您达到当天的目标), 为此, 请使用 Button
的 enabled
参数
Button(onClick = { count++ }, Modifier.padding(top = 8.dp), enabled = count < 10) {
if(count >= 10){
Text("Enough")
}else{
Text(text = "Add")
}
}
状态提升
上文中, 将 count
存储在组件中, 该组件即被称作有状态组件, 但是,具有内部状态的可组合项往往不易重复使用,也更难测试 , 这是事实, 因为组件如果带了状态的话那么大部分情况下它应该比较特殊,至少重复使用的概率会比较低
原本:
kotlin
@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
var count by rememberSaveable {
mutableStateOf(0)
}
Column {
if (count > 0) {
Text("You've had $count glasses.")
}
Button(onClick = { count++ }, Modifier.padding(top = 8.dp), enabled = count < 10) {
Text("Add one")
}
}}
无状态化后:
```kotlin
@Composable
fun WaterStateCounter(modifier: Modifier = Modifier) {
var count by rememberSaveable {
mutableStateOf(0)
}
WaterCounter(count, { count++ })
}
@Composable
fun WaterCounter(count:Int, onCountChange: () -> Unit, modifier: Modifier = Modifier){
Column {
if (count > 0) {
Text("You've had $count glasses.")
}
Button(onClick = { onCountChange() }, Modifier.padding(top = 8.dp), enabled = count < 10) {
Text("Add one")
}
}}
```
现在 WaterCount 的复用性更高了, 并且在 WaterStateCounter 中如果有其它组件需要调用 count 值也更为方便了
并且重组是对单组件发生的, WaterStateCount 所依赖的变量未发生变化的话整个组件则只有依赖了 count 的组件会发生重组(在 count 修改时)
摘抄: 提升状态时,有三条规则可帮助您弄清楚状态应去向何处:
- 状态应至少提升到使用该状态(读取)的所有可组合项的最低共同父项。
- 状态应至少提升到它可以发生变化(写入)的最高级别。
- 如果两种状态发生变化以响应相同的事件,它们应提升到同一级别