kotlin - 在 Android Compose 的 View 模型中使用 State

我已经阅读了一些学习 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)在可组合项中。

要获取更新,我们必须使用响应式数据,例如 FlowLiveDataState 及其变体。

3。使用 StateFlowState

来自docs ,可以看到compose支持FlowLiveDataRxJava

您可以在用法中看到我们正在为 StateFlow 使用 collectAsState()。 该方法将 StateFlow 转换为 State。所以两者都可以使用。

如果 ViewModel 之外的层(如 repo)是数据源并且它们使用 Flow 数据类型,则使用 Flow
否则 MutableState 应该没问题。

关于kotlin - 在 Android Compose 的 View 模型中使用 State<Boolean> 是一种好方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69763249/

相关文章:

sorting - 如何按索引对 Vec 进行排序?

python - 如何并排放置两个或多个 ASCII 图像?

postgresql - 在 PostgreSQL 中存储 UUID 的最佳数据类型是什么?

google-cloud-platform - 如何继承访问权限以查看属于 GCP 组织的所有项目?

r - flextable 中有条件的粗体值

javascript - React 中的 for 循环

r - 如何从 R 中的多列创建合并值的新数据框

java - dockerBuild 失败导致不支持的类文件主要版本 61 错误

java - 从 Double.toLongBits 创建的 long 中获取 double

r - pivot_longer 对于具有相同 names_to 的多个集合