我想重构我的自定义视图以使用 android 架构组件。不过,我看到了
ViewModelProviders.of(...)
仅需要 Activity 或片段。知道如何让它发挥作用吗?我应该使用片段而不是自定义视图吗?
可以在 View 中获取 ViewModel 实例,但不推荐这样做。根据这篇文章:
虽然在 Activity 或 Fragment 中获取 ViewModel 很容易,但在 View 中获取此实例却并不简单。这背后的主要原因是视图应该独立于所有处理,即使您的所有逻辑都位于 ViewModel 内,但您在视图内访问该 ViewModel 的事实使其依赖于不应该依赖的东西。控制 View 的推荐方法是根据 Fragment 或 Activity 中 ViewModel 的状态向其传递参数。
重点是尝试从上下文中获取 Activity:
override val activity: FragmentActivity by lazy {
try {
context as FragmentActivity
} catch (exception: ClassCastException) {
throw ClassCastException("Please ensure that the provided Context is a valid FragmentActivity")
}
}
override var viewModel = ViewModelProvider(activity).get(SharedViewModel::class.java)
正如所提到的想法,如果可能的话,我会尽量避免这种方法。
您可以使用
findViewTreeViewModelStoreOwner()
从自定义 ViewModel
中检索 View
。
然后在该视图中实现
DefaultLifecycleObserver
,以便您可以挂钩生命周期方法。
这 2 个组合应该为您提供通过
Activity
和 Fragment
实现`获得的所有 ViewModel 和 Scope 选项。
自定义视图代码,这里它基于
ConstraintLayout
,我添加了KoinViewModelFactory
,以便可以通过构造函数注入您的依赖项。否则就使用 ViewModelProvider(owner)
。
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.lifecycle.viewModelScope
import org.koin.androidx.viewmodel.factory.KoinViewModelFactory
import org.koin.core.component.getScopeId
import org.koin.core.qualifier.named
import org.koin.mp.KoinPlatform.getKoin
class MyCustomView : ConstraintLayout, DefaultLifecycleObserver {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
private val viewModel by lazy {
findViewTreeViewModelStoreOwner()?.let { owner ->
ViewModelProvider(
owner,
KoinViewModelFactory(
kClass = MyCustomViewModel::class,
scope = getKoin().getOrCreateScope(scopeId = owner.getScopeId(), named(owner.getScopeId())),
)
)[MyCustomViewModel::class.java]
}
}
init {
inflate(context, R.layout.my_custom_view, this)
// makes this view lifecycle aware
(context as? LifecycleOwner)?.lifecycle?.addObserver(this)
}
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
// observe changes
viewModel?.events?.observeNonNull(owner) { handleEvent(it) }
}
override fun onDestroy(owner: LifecycleOwner) {
// other lifecycle methods like this are available...
}
fun handleEvent(event: UiEvent) {}
}
ViewModel 实现
import androidx.lifecycle.ViewModel
import androidx.lifecycle.MutableLiveData
import org.koin.core.component.KoinComponent
class MyCustomViewModel(
private val injectedService: SomeService, // will be injected
) : ViewModel(), KoinComponent {
val events = MutableLiveData<UiEvent>() // live data example
fun someAction() {
events.value = UiEvent.SomeAction
}
sealed class UiEvent {
data object SomeAction : UiEvent()
}
}