我应该如何在两个片段中使用 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?的主要内容,如果未能解决你的问题,请参考以下文章
如何通过 viewModels 获取 viewModel? (片段-ktx)
Android 调用组件 w/listener 或让 viewmodel 调用组件与片段通信