我应该如何在两个片段中使用 ViewModel?
How should i use ViewModel in two fragments?
我有一个包含一个 activity 和两个片段的应用程序,在第一个片段中,我应该能够将数据插入数据库,在第二个片段中,我应该能够在recyclerView.
所以我制作了数据库、我的 RecyclerView 适配器和 ViewModel,
现在的问题是我应该如何管理所有这些?
我是否应该在 activity 中初始化 ViewModel 并以某种方式从片段中调用它以使用插入?
我应该在两个片段中初始化视图模型两次吗?
我的代码如下所示:
假设我在 Activity:
中初始化了 viewholder
class MainActivity : AppCompatActivity() {
private val articoliViewModel: ArticoliViewModel by viewModels {
ArticoliViewModelFactory((application as ArticoliApplication).repository)
}
}
然后我的 FirstFragments 方法应该使用 viewModel 将数据添加到数据库,如下所示:
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))
}
}
还有我的 SecondFragment
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")
}
}
多个片段是否应该共享一个 ViewModel 取决于它们是否显示相同的数据。如果它们显示相同的数据,我认为共享一个 ViewModel 通常是有意义的,这样当您在它们之间切换时就不必从存储库中提取数据,因此转换速度更快。如果它们中的任何一个也有大量的唯一数据,您可以考虑将其分解为一个单独的 ViewModel,这样它就不会在不需要时占用内存。
假设您使用的是共享 ViewModel,您可以至少选择两种不同的方式之一,具体取决于您喜欢的代码风格。封装和代码复制之间有一种小的权衡,尽管它并没有真正封装,因为它们正在查看同一个实例。所以就个人而言,我更喜欢第二种方式。
- 每个 ViewModel 直接创建 ViewModel。如果您使用
by activityViewModels()
,则 ViewModel 的范围将限定为 Activity,因此它们将接收相同的实例。但是由于你的 ViewModel 需要一个自定义工厂,你必须在两个 Fragments 中指定它,所以有一点代码重复:
// 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
或者为避免代码重复,您可以为片段创建一个扩展 属性,这样它们就不必重复代码:
val Fragment.articoliViewModel: ArticoliViewModel
get() = (activity as MainActivity).articoliViewModel
我有一个包含一个 activity 和两个片段的应用程序,在第一个片段中,我应该能够将数据插入数据库,在第二个片段中,我应该能够在recyclerView.
所以我制作了数据库、我的 RecyclerView 适配器和 ViewModel,
现在的问题是我应该如何管理所有这些?
我是否应该在 activity 中初始化 ViewModel 并以某种方式从片段中调用它以使用插入?
我应该在两个片段中初始化视图模型两次吗?
我的代码如下所示:
假设我在 Activity:
中初始化了 viewholderclass MainActivity : AppCompatActivity() {
private val articoliViewModel: ArticoliViewModel by viewModels {
ArticoliViewModelFactory((application as ArticoliApplication).repository)
}
}
然后我的 FirstFragments 方法应该使用 viewModel 将数据添加到数据库,如下所示:
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))
}
}
还有我的 SecondFragment
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")
}
}
多个片段是否应该共享一个 ViewModel 取决于它们是否显示相同的数据。如果它们显示相同的数据,我认为共享一个 ViewModel 通常是有意义的,这样当您在它们之间切换时就不必从存储库中提取数据,因此转换速度更快。如果它们中的任何一个也有大量的唯一数据,您可以考虑将其分解为一个单独的 ViewModel,这样它就不会在不需要时占用内存。
假设您使用的是共享 ViewModel,您可以至少选择两种不同的方式之一,具体取决于您喜欢的代码风格。封装和代码复制之间有一种小的权衡,尽管它并没有真正封装,因为它们正在查看同一个实例。所以就个人而言,我更喜欢第二种方式。
- 每个 ViewModel 直接创建 ViewModel。如果您使用
by activityViewModels()
,则 ViewModel 的范围将限定为 Activity,因此它们将接收相同的实例。但是由于你的 ViewModel 需要一个自定义工厂,你必须在两个 Fragments 中指定它,所以有一点代码重复:
// 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
或者为避免代码重复,您可以为片段创建一个扩展 属性,这样它们就不必重复代码:
val Fragment.articoliViewModel: ArticoliViewModel
get() = (activity as MainActivity).articoliViewModel