首页 > 解决方案 > 查看 Recycler 的模型查看项目

问题描述

我的活动有一个谷歌的 ViewModel 来获取一些模型项目。然后将这些项目转换为 RecyclerView 的适配器项目。一个 RecyclerView 还支持多种类型的适配器项。

我想为这些模型对象中的每一个拥有单独的视图模型对象,以便我可以将更复杂的逻辑封装在那个“小”视图模型中。

目前,当我有一些仅与某些适配器项相关的异步逻辑(需要在 onCleared() 中停止)时,我必须以某种方式通过主视图模型路由回调,以便正确取消注册所有内容。

我正在考虑使用ViewModelProvider::get(key, modelClass),但我的物品会随着时间的推移而变化,我找不到“清除”旧物品的好方法。

您如何在项目中处理这些案例?

编辑:要添加有关我的关注的更多信息,也许用不同的词:我希望我的“小” ViewModel 与它所代表的模型项一样长。代表着:

编辑:请尝试将其与以片段作为项目的 ViewPager 进行比较。每个单独的模型项都表示为带有其 ViewModel 的片段。我想实现类似的东西,但对于 RecyclerView。

标签: androidandroid-recyclerviewandroid-architecture-componentsandroid-viewmodel

解决方案


默认情况下, androidx.lifecycle.ViewModel并不打算用于 RecyclerView 项目

为什么?

ViewModel是 AAC (Android 架构组件) ,其唯一目的是在 Android Activity/Fragment生命周期的配置更改中幸存下来,以便在这种情况下可以通过 ViewModel 持久化数据。

这是通过在与托管活动相关的存储中缓存 VM 实例来实现的。

这就是为什么不应该直接在 RecyclerView (ViewHolder)项目上使用它的原因,因为项目视图本身将是 Activity/Fragment 的一部分,并且它(RecyclerView/ViewHolder)不包含要提供的任何特定 API (ViewModel 基本上是从中派生的对于给定的 Activity/Fragment 实例)ViewModelStoreOwner

获取 ViewModel 的简单语法是:

ViewModelProvider(this).get(ViewModel::class.java)

& 这里this将被称为 Activity/Fragment 上下文。

因此,即使您最终ViewModelRecyclerViewItems 中使用,它也会给您相同的实例,因为上下文可能是 Activity/Fragment 在 RecyclerView 中是相同的,这对我来说没有意义。所以 ViewModel 对 RecyclerView 没用,或者它对这种情况没有多大贡献。


TL;博士

解决方案?

您可以直接传入LiveData您需要从课堂ViewModel中的 Activity/Fragment 观察的对象RecyclerView.Adapter。您还需要提供LifecycleOwner适配器以开始观察给定的实时数据。

因此,您的 Adapter 类将如下所示:

class RecyclerViewAdapter(private val liveDataToObserve: LiveData<T>, private val lifecycleOwner: LifecycleOwner) : RecyclerView.Adapter<ViewHolder>() {
    
    init {
        liveDataToObserve.observe(lifecycleOwner) { t ->
            // Notify data set or something...
        }
    }

}

如果不是这种情况,并且您希望将它放在ViewHolder课堂上,那么您可以在方法LiveData期间将您的对象onCreateViewHolderlifecycleOwner.

加分!

如果您在 RecyclerView 项目上使用数据绑定,那么您可以轻松地lifecyclerOwner从绑定类中获取对象。您需要做的就是在onCreateViewHolder()下面设置它:

class RecyclerViewAdapter(private val liveDataToObserve: LiveData<T>, private val lifecycleOwner: LifecycleOwner) : RecyclerView.Adapter<ViewHolder>() {
    
    override fun onCreateViewHolder: ViewHolder {
        // Some piece of code for binding
        binding.lifecycleOwner = this@RecyclerViewAdapter.lifecycleOwner
        // Another piece of code and return viewholder
    }

}

class ViewHolder(private val someLiveData: LiveData<T>, binding: ViewDataBinding): RecyclerView.ViewHolder(binding.root) {
    
    init {
        someLiveData.observe(requireNotNull(binding.lifecycleOwner)) { t->
            // set your UI by live data changes here
        }
    }
}

所以是的,您可以为您的ViewHolder实例使用包装类来为您提供LiveData开箱即用的功能,但如果包装类正在扩展ViewModel类,我会不鼓励它。

只要担心模仿onCleared()的方法ViewModel,您就可以在包装类上创建一个方法,该方法在ViewHolder被回收或通过方法onViewRecycled()onViewDetachedFromWindow()最适合您的情况的任何方法从窗口分离时被调用。


编辑@Mariusz 的评论:关于使用Activity/Fragment作为LifecycleOwner的担忧是正确的。但将其解读为 POC 可能会产生轻微的误解。

只要有人在给定项目lifecycleOwner中观察,就可以这样做,因为它是生命周期感知组件,它在内部处理对生命周期的订阅,因此可以安全使用。即使您可以根据需要显式删除观察,也可以使用or方法。LiveDataRecyclerViewHolderLiveDataonViewRecycled()onViewDetachedFromWindow()

关于里面的异步操作ViewHolder

  1. 如果您正在使用协程,那么您可以使用lifecycleScopefromlifecycleOwner来调用您的操作,然后将数据提供回特定的观察LiveData,而无需明确处理清除案例LifecycleScope将为您处理它)

  2. 如果不使用协程,那么您仍然可以进行异步调用并将数据返回给观察者,而LiveData不必担心在onViewRecycled()onViewDetachedFromWindow()回调期间清除异步操作。这里重要的是LiveData它尊重 given 的生命周期LifecycleOwner,而不是正在进行的异步操作。


推荐阅读