我应该如何在两个片段中使用 ViewModel?

Posted

技术标签:

【中文标题】我应该如何在两个片段中使用 ViewModel?【英文标题】:How should i use ViewModel in two fragments? 【发布时间】:2021-04-23 21:00:00 【问题描述】:

我有一个包含一个活动和两个片段的应用程序,在第一个片段中,我应该能够将数据插入数据库,在第二个片段中,我应该能够在 recyclerView 中看到添加的项目。

所以我已经制作了数据库、我的 RecyclerView 适配器和 ViewModel,

现在的问题是我应该如何管理这一切?

我是否应该在 Activity 中初始化 ViewModel 并从片段中以某种方式调用它以使用插入?

我应该在两个片段中初始化视图模型两次吗?

我的代码如下所示:

假设我在我的 Activity 中初始化了 viewholder:

class MainActivity : AppCompatActivity() 
     private val articoliViewModel: ArticoliViewModel by viewModels 
        ArticoliViewModelFactory((application as ArticoliApplication).repository)
    

然后我应该使用 viewModel 将数据添加到数据库的 FirstFragments 方法如下所示:

class FirstFragment : Fragment() 
    private val articoliViewModel: ArticoliViewModel by activityViewModels()
    private fun addArticolo(barcode: String, qta: Int)  // function which add should add items on click
      // here i should be able to do something like this

        articoliViewModel.insert(Articolo(barcode, qta))
    

还有我的第二个片段

class SecondFragment : Fragment()     
    private lateinit var recyclerView: RecyclerView
    private val articoliViewModel: ArticoliViewModel by activityViewModels()
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)

        recyclerView = view.findViewById(R.id.recyclerView)
        val adapter = ArticoliListAdapter()
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(activity)
        // HERE I SHOULD BE ABLE DO THIS   
        articoliViewModel.allWords.observe(viewLifecycleOwner)  articolo->
            articolo.let  adapter.submitList(it) 
        

     

编辑:

我的 ViewModel 如下所示:

class ArticoliViewModel(private val repository: ArticoliRepository): ViewModel() 
    val articoli: LiveData<List<Articolo>> = repository.articoli.asLiveData()

    fun insert(articolo: Articolo) = viewModelScope.launch 
        repository.insert(articolo)
    


class ArticoliViewModelFactory(private val repository: ArticoliRepository): ViewModelProvider.Factory 
    override fun <T : ViewModel?> create(modelClass: Class<T>): T 
        if (modelClass.isAssignableFrom(ArticoliViewModel::class.java)) 
            @Suppress("UNCHECKED_CAST")
            return ArticoliViewModel(repository) as T
        
        throw IllegalArgumentException("Unknown ViewModel class")
    


【问题讨论】:

如果你初始化视图模型两次,你将有两个单独的实例,我认为这违背了你的目的。 @KristyWelsh 所以此时我应该像在示例中那样在 Activity 中执行此操作,但是一旦在 Activity 中初始化,我如何在 Fragment 中使用它? 是什么阻止您为每个 Fragment 使用不同类型的 ViewModel?他们都可以从同一个存储库中观察数据源 @IvanWooll 实际上我问的是在片段中初始化两个 ViewModel 是否是一个好习惯 好的,所以如果你的 Fragment 尝试先实例化它,它没有使用自定义工厂,所以它失败了。我会写一个更详细的答案。 【参考方案1】:

多个 Fragment 是否应该共享一个 ViewModel 取决于它们是否显示相同的数据。如果它们显示相同的数据,我认为共享 ViewModel 通常是有意义的,因此当您在它们之间切换时不必从存储库中提取数据,因此转换速度更快。如果它们中的任何一个也有大量的唯一数据,您可以考虑将其拆分为单独的 ViewModel,这样它就不会在不需要时占用内存。

假设您使用的是共享 ViewModel,您可以使用至少两种不同的方式之一,具体取决于您喜欢的代码样式。封装和代码重复之间有一种小的权衡,尽管它并没有真正封装,因为它们正在查看同一个实例。所以就个人而言,我更喜欢第二种方式。

    每个 ViewModel 直接创建 ViewModel。如果您使用by activityViewModels(),那么 ViewModel 将被限定为 Activity,因此它们都将收到相同的实例。但是由于您的 ViewModel 需要自定义工厂,因此您必须在两个 Fragment 中都指定它,因此存在一些代码重复:
// In each Fragment:
private val articoliViewModel: ArticoliViewModel by activityViewModels 
    ArticoliViewModelFactory((application as ArticoliApplication).repository)

    在 MainActivity 中指定一次 ViewModel,然后通过强制转换 Activity 在 Fragments 中访问它。
// In Activity: The same view model code you already showed in your Activity, but not private

// In Fragments:
private val articoliViewModel: ArticoliViewModel
    get() = (activity as MainActivity).articoliViewModel

或者为了避免代码重复,您可以为您的 Fragment 创建一个扩展属性,这样它们就不必有这种代码重复:

val Fragment.articoliViewModel: ArticoliViewModel
    get() = (activity as MainActivity).articoliViewModel

【讨论】:

最终通过使用片段中的第二个解决方案,我无法使用您在第一个中提到的应用程序,即使是第三个也可以,谢谢。现在我遇到了“无法访问主线程上的数据库”的问题,但我想提出一个新问题。 其实,看这里。今天的那个问题是重复的,答案很差。 ***.com/questions/44167111/… 查看 Kotlin Coroutines 的答案,因为接受的答案已经过时了。 我实际上一步一步地跟着this official guide,在示例中数据插入没有问题,它使用协程..

以上是关于我应该如何在两个片段中使用 ViewModel?的主要内容,如果未能解决你的问题,请参考以下文章

使用新的架构组件 ViewModel 在片段之间共享数据

如何通过 viewModels 获取 viewModel? (片段-ktx)

Android 调用组件 w/listener 或让 viewmodel 调用组件与片段通信

如何转换 ViewModel 以利用状态保存?

如何将editText值传递给viewModel和Livedata(Kotlin)

Android MVVM在哪里存储数据?