我已经阅读了一些学习 Compose 的示例代码。
我发现许多示例项目使用代码 A 在 View 模型中创建一个 StateFlow
,然后在 @Composable
函数中将其转换为 State
,当 drawerOpen
改变时,UI 会自动更新。
1:我觉得Code B和Code C都可以做同样的事情,对吧?为什么很多项目很少用到它们?
2:代码A是好方法吗?
3: 我不需要为 @Composable fun myRoute(...)
中的变量 drawerOpen
添加 rememberSaveable
因为 View 模型会存储数据,对吧?
代码A
class MainViewModel : ViewModel() {
private val _drawerShouldBeOpened = MutableStateFlow(false)
val drawerShouldBeOpened: StateFlow<Boolean> = _drawerShouldBeOpened
...
}
@Composable
fun myRoute(
val drawerOpen by MainViewModel.drawerShouldBeOpened.collectAsState() //Do I need add rememberSaveable ?
...
}
代码B
class MainViewModel : ViewModel() {
private var _drawerShouldBeOpened = mutableStateOf(false)
val drawerShouldBeOpened: State<Boolean> = _drawerShouldBeOpened
...
}
@Composable
fun myRoute(
val drawerOpen = MainViewModel.drawerShouldBeOpened //Do I need add rememberSaveable ?
...
}
代码 C
class MainViewModel : ViewModel() {
private var _drawerShouldBeOpened = false
val drawerShouldBeOpened: Boolean = _drawerShouldBeOpened
...
}
@Composable
fun myRoute(
val drawerOpen = rememberSaveable { mutableStateOf(MainViewModel.drawerShouldBeOpened)) //Can I remove rememberSaveable ?
}
最佳答案
这里有多个问题。
让我尽可能回答。
1。你应该在哪里使用remember
/rememberSaveable
? (代码 A、B 或 C)
仅在代码 C 中是必需的。
(在代码 A 和 B 中使用也没有问题,但没有优势)
原因,
在代码 A 和 B 中——状态在 View 模型中维护。因此,该值在重组后仍然存在。
但在代码 C 中,状态是在可组合项内部创建和维护的。因此,记住
是使值在重组后仍然存在所必需的。
More details in Docs
2。为什么Code C用得不多?
只要状态发生变化而不是值发生变化,就会发生可组合重组。
下面给出了一个简单的例子来证明这一点。
class ToggleViewModel : ViewModel() {
private val _enabledStateFlow = MutableStateFlow(false)
val enabledStateFlow: StateFlow<Boolean> = _enabledStateFlow
private val _enabledState = mutableStateOf(false)
val enabledState: State<Boolean> = _enabledState
private var _enabled = false
val enabled: Boolean = _enabled
fun setEnabledStateFlow(isEnabled: Boolean) {
_enabledStateFlow.value = isEnabled
}
fun setEnabledState(isEnabled: Boolean) {
_enabledState.value = isEnabled
}
fun setEnabled(isEnabled: Boolean) {
_enabled = isEnabled
}
}
@Composable
fun BooleanToggle(
viewmodel: ToggleViewModel = ToggleViewModel(),
) {
val enabledStateFlow by viewmodel.enabledStateFlow.collectAsState()
val enabledState by viewmodel.enabledState
val enabled by rememberSaveable {
mutableStateOf(viewmodel.enabled)
}
Column {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
) {
Text(text = if (enabledStateFlow) {
"Enabled"
} else {
"Disabled"
})
Button(onClick = { viewmodel.setEnabledStateFlow(!enabledStateFlow) }) {
Text("Toggle State Flow")
}
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
) {
Text(text = if (enabledState) {
"Enabled"
} else {
"Disabled"
})
Button(onClick = { viewmodel.setEnabledState(!enabledState) }) {
Text("Toggle State")
}
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
) {
Text(text = if (enabled) {
"Enabled"
} else {
"Disabled"
})
Button(onClick = { viewmodel.setEnabled(!enabled) }) {
Text("Toggle Value")
}
}
}
}
你可以看到第三个文本不会在点击按钮时更新。
原因是可组合项内的可变状态是使用 View 模型数据中的初始值创建的。但对该数据的进一步更新不会反射(reflect)在可组合项中。
要获取更新,我们必须使用响应式数据,例如 Flow
、LiveData
、State
及其变体。
3。使用 StateFlow
与 State
。
来自docs ,可以看到compose支持Flow
、LiveData
和RxJava
。
您可以在用法中看到我们正在为 StateFlow
使用 collectAsState()
。
该方法将 StateFlow
转换为 State
。所以两者都可以使用。
如果 ViewModel
之外的层(如 repo)是数据源并且它们使用 Flow
数据类型,则使用 Flow
。
否则 MutableState
应该没问题。
关于kotlin - 在 Android Compose 的 View 模型中使用 State<Boolean> 是一种好方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69763249/