我现在正处于这样的阶段:我希望将我的StateFlow<Model>()公开到普通的BaseViewModel()类中,以便在它上实现公共操作,而不会在其他ViewModels实现中重复它们。最后,我有了一个想法,但在构建PoC时,我遇到了一些限制。我的想法及其局限性如下,欢迎您对原产地问题的解决方案。
我将我的模型分成interface和一个具体的实现。
sealed interface Model {
var isLoading: Boolean
var errors: List<String>
/**
* @param obj Should has the same concrete type as concrete type of object which copy() it invokes
* */
fun copy(obj: Model): Model
}
data class DeputiesModel(
override var isLoading: Boolean = false,
override var errors: List<String> = emptyList<String>(),
var deputies: List<Deputy> = emptyList()
) : Model {
override fun copy(obj: Model): DeputiesModel {
if (obj !is DeputiesModel)
throw IllegalArgumentException("Passed object implements ${Model::javaClass.name}" +
" interface, but should be concrete ${DeputiesModel::javaClass::name} implementation.")
return this.copy(deputies = obj.deputies, isLoading = obj.isLoading, errors = obj.errors)
}
}我需要一个copy()方法,在接口data class copy()是关闭的重载和覆盖。
我的StateFlow实现已在BaseViewModel()中移动
abstract class BaseViewModel<T : Model> : ViewModel() {
protected lateinit var state: MutableStateFlow<T>
lateinit var uiState: StateFlow<T>
}我在这里添加了泛型,以避免将Model类型转换为它的具体实现之一,例如,在继承BaseViewModel的类中使用DeputiesModel,否则这些额外的代码将使公开通用方法成为多余。
以下是第一种常见的方法:
fun removeShownError() {
state.update { state ->
state.errors = state.errors.filter { str -> !str.equals(state.errors.first()) }
state.copy(state) as T
}
}该设计与实现的局限性在于,当原始参数化state.copy(state)进行uiState.collectLatest{}调用时,state.copy(isLoading = false)不触发state.copy(isLoading = false)调用。我还没找到根本原因。
val viewModel: DeputiesViewModel by viewModels { viewModelFactory }
lifecycleScope.launch {
viewModel.uiState.collectLatest { it ->
if (it.errors.isNotEmpty()) {
showError(
view = requireActivity().findViewById(R.id.nav_view),
text = it.errors.first(),
onDismiss = { viewModel.removeShownError() }
)
}
(binding.list.adapter as DeputiesAdapter).update(it.deputies)
}
}就这样。你的想法很受欢迎。
发布于 2022-07-07 20:18:04
下面是如何用扩展函数来解决公共函数removeShownError()的示例,这样就不需要BaseViewModel了。
没有可变性的模型类:
sealed interface Model {
val isLoading: Boolean
val errors: List<String>
}
data class DeputiesModel(
override val isLoading: Boolean = false,
override val errors: List<String> = emptyList<String>(),
val deputies: List<Deputy> = emptyList()
) : Model 用于共享功能的扩展函数,在顶层定义:
// Since this is a sealed interface, you can create a single extension function to
// implement clearing errors for each of the implementations. This will be cleaner
// than having to deal with wrong input type problem in your current copy function
// implementation.
@Suppress("UNCHECKED_CAST")
fun <T: Model> T.withNewErrors(newErrors: List<String>): T = when (this) {
is DeputiesModel -> copy(errors = newErrors)
//... other types
} as T
fun <T: Model> MutableStateFlow<T>.removeShownError() {
update { state ->
state.withNewErrors(
state.errors.filter { str -> !str.equals(state.errors.first()) }
)
}
}现在不需要BaseViewModel了。您只需向具有相关流的任何ViewModel添加:
fun removeShownError() = state.removeShownError()顺便说一句,在lateinit中使用ViewModel没有多大意义。这是像活动这样的类所需要的黑客,在这些类中,大多数初始化变量所需的操作都需要上下文,但是在类实例化时,上下文还没有准备好,而且create()是您自己的任何属性将被用于任何东西的最早点。在ViewModel中,您可能需要使用的所有东西都可以通过构造函数在类初始化时使用,因此没有理由推迟属性的初始化。
即使您必须为此使用lateinit,也没有必要将其用于它的合作伙伴不可变的公共属性,因为该属性可以只使用自定义getter,因此它首先没有要初始化的支持变量:
val uiState: StateFlow<T> get() = statehttps://stackoverflow.com/questions/72884134
复制相似问题