用于自定义视图的 Android ViewModel

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

我想重构我的自定义视图以使用 android 架构组件。不过,我看到了

ViewModelProviders.of(...)

仅需要 Activity 或片段。知道如何让它发挥作用吗?我应该使用片段而不是自定义视图吗?

android android-custom-view android-architecture-components
2个回答
0
投票

可以在 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)

正如所提到的想法,如果可能的话,我会尽量避免这种方法。


0
投票

您可以使用

findViewTreeViewModelStoreOwner()
从自定义
ViewModel
中检索
View

然后在该视图中实现

DefaultLifecycleObserver
,以便您可以挂钩生命周期方法。

这 2 个组合应该为您提供通过

Activity
Fragment
实现`获得的所有 ViewModel 和 Scope 选项。

Kotlin 代码示例

自定义视图代码,这里它基于

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()
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.