在 Jetpack Compose 上处理状态的最佳方式是什么/在哪里?

问题描述 投票:0回答:2

我见过一些 Jetpack Compose 项目,并且见过两种类型的管理状态,但没有意识到哪一种更好。

例如,我们假设:输入状态。我见过人们在 UI 中管理此状态,使用 Remember 来保存值的状态。

我见过的另一种方法是在 ViewModel 中创建此 mutableState 并从那里存储/使用它。最好的方法是什么?

android kotlin android-jetpack-compose android-viewmodel compose-recomposition
2个回答
4
投票

除了@Thracian的回答。

让我根据我目前的 Jetpack Compose 经验水平来分享我的思考过程。只是免责声明,我仍处于学习曲线中。

IMO,不存在“最好”这样的东西,我们领域的事物在不断发展,今天被认为是“最好”的东西明天可能会过时,但是有一些被社区“推荐”、批准和采用的做法可能会被社区所“推荐”、批准和采用。使您免于处理一些陷阱(例如不需要的重新组合、无限的导航主机调用(您已经处理过这个)等..),但是否遵循它取决于您。

所以你想要理解的就是“状态提升”。我可以通过简单地采样一个场景来解释这一点(这也是基于我自己在 Jetpack Compose 中应用知识的经验)。 考虑具有 3 种不同复杂程度的登录用例

登录 UI 原型:— 只是展示您潜在的登录屏幕设计和用户交互
  • 登录 UI 模型:— 经过一些验证和一些显示负面场景的吐司,只是原型的高级版本
  • 一个完全工作的登录模块 - 您必须在其中构建视图模型,将事物绑定到生命周期,执行并发操作等......
  • 此时,您已经了解了基于上述用例的不同级别的状态管理。

对于登录原型,我不需要状态类或视图模型,因为它只是一个原型

@Composable fun LoginScreen() { val userName by remember { <mutable string state username> } val password by remember { <mutable string state password> } Column { Text(text = username) Text(text = password) Button("Login") } }

而且因为它是一个非常简单的 UI(可组合),我只需要使用记住 + 状态指定可组合的基本结构,展示正在发生的输入。

对于具有简单验证的登录模型,我们使用了推荐的状态提升,使用类,

class LoginState { var event; var mutableUserNameState; var mutablePasswordState; fun onUserNameInput() {...} fun onPasswordInput() {...} fun onValidate() { if (not valid) { event.emit(ShowToast("Not Valid")) } else { event.emit(ShowToast("Valid")) } } } @Composable fun LoginScreen() { val loginState by remember { LoginState } LaunchedEffect() { event.observe { it.ShowToast() } } Column { Text(text = loginState.mutableUserNameState, onInput = { loginState.onUserNameInput()} ) Text(text = loginState.mutablePasswordState, onInput = { loginState.onPasswordInput()} ) Button(loginState.onValidate) } }

现在是一个完整的登录模块,您还需要考虑生命周期范围

class LoginViewModel( val userRepository: UserRepository // injected by your D.I framework ): ViewModel { var event; var mutableUserNameState; var mutablePasswordState; fun onUserNameInput() {...} fun onPasswordInput() {...} fun onValidateViaNetwork() { // do a non-blocking call to a server viewModelScope.launch { var isUserValid = userRepository.validate(username, password) if (isUserValid) { event.emit(ShowToast("Valid")) } else { event.emit(ShowToast("Not Valid")) } } } } @Composable fun LoginScreen() { val userNameState by viewModel.mutableUserNameState val passwordState by viewModel.mutablePasswordState LaunchedEffect() { event.observe { it.ShowToast() } } Column { Text(text = userNameState, onInput = { viewModel.onUserNameInput()} ) Text(text = passwordState, onInput = { viewModel.onPasswordInput()} ) Button(viewModel.onValidateViaNetwork) } }

再次强调,这只是基于我的经验以及我如何决定提升我的状态。至于我包含的片段,我尝试使它们尽可能伪,而不使它们看起来脱离上下文,因此它们不可编译。另外,模拟和原型被认为是相同的,我只是结合使用它们来将事物放入上下文中。


2
投票

@Composable fun rememberScrollState(initial: Int = 0): ScrollState { return rememberSaveable(saver = ScrollState.Saver) { ScrollState(initial = initial) } } @Stable class ScrollState(initial: Int) : ScrollableState { /** * current scroll position value in pixels */ var value: Int by mutableStateOf(initial, structuralEqualityPolicy()) private set

//其余代码
}

这是 Jetpack Compose 中的常见方法。我在我构建的库中使用这种方法,例如在这个

图像裁剪库

中,我保持状态和动画。 Animatable 是低级默认动画类,也拥有自己的状态。 @Suppress("NotCloseable") class Animatable<T, V : AnimationVector>( initialValue: T, val typeConverter: TwoWayConverter<T, V>, private val visibilityThreshold: T? = null ) { internal val internalState = AnimationState( typeConverter = typeConverter, initialValue = initialValue ) /** * Current value of the animation. */ val value: T get() = internalState.value /** * Velocity vector of the animation (in the form of [AnimationVector]. */ val velocityVector: V get() = internalState.velocityVector /** * Returns the velocity, converted from [velocityVector]. */ val velocity: T get() = typeConverter.convertFromVector(velocityVector) /** * Indicates whether the animation is running. */ var isRunning: Boolean by mutableStateOf(false) private set /** * The target of the current animation. If the animation finishes un-interrupted, it will * reach this target value. */ var targetValue: T by mutableStateOf(initialValue) private set }

等等。这种方法适用于不涉及业务逻辑但涉及 Ui 逻辑的 ui 组件。

当您需要根据业务逻辑(例如搜索或从 API 获取结果)更新 Ui 时,您应该使用 Presenter 类,该类也可以是 ViewModel。

最后但最不重要的一点是,人们现在质疑是否应该有一个带有 Jetpack Compose 的 ViewModel,因为我们可以使用带有 AAC ViewModel 的状态。而且cashapp推出了

分子库

,你也可以看看。 此外,有关

状态持有者

的链接也是很好的阅读来源

© www.soinside.com 2019 - 2024. All rights reserved.