Android 如何在两个片段中使用ViewModel?

Android 如何在两个片段中使用ViewModel?,android,kotlin,android-fragments,android-viewmodel,Android,Kotlin,Android Fragments,Android Viewmodel,我有一个包含一个活动和两个片段的应用程序,在第一个片段中,我应该能够将数据插入数据库,在第二个片段中,我应该能够在recyclerView中看到添加的项目 所以我制作了数据库、我的RecyclerView适配器和ViewModel 现在的问题是,我应该如何管理这一切 我是否应该在活动中初始化ViewModel,并从片段中以某种方式调用它以使用insert 我应该在两个片段中初始化viewmodel两次吗 我的代码如下所示: class FirstFragment : Fragment() {

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

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

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

我是否应该在活动中初始化ViewModel,并从片段中以某种方式调用它以使用insert

我应该在两个片段中初始化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))
    }
}
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")
    }

}
假设我在活动中初始化了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 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")
    }

}
还有我的第二个碎片

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 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 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")
    }

}
类ArticoliViewModel(专用val存储库:ArticoliRepository):ViewModel(){
val articoli:LiveData=repository.articoli.asLiveData()
有趣的插入(articolo:articolo)=viewModelScope.launch{
存储库.插入(articolo)
}
}
类ArticoliViewModelFactory(专用val存储库:ArticoliRepository):ViewModelProvider.Factory{
重写趣味创建(modelClass:Class):T{
if(modelClass.isAssignableFrom(articliviewmodel::class.java)){
@抑制(“未选中的_CAST”)
将ArticoliViewModel(存储库)返回为T
}
抛出IllegalArgumentException(“未知ViewModel类”)
}
}

多个片段是否应共享一个ViewModel取决于它们是否显示相同的数据。如果它们显示相同的数据,我认为共享一个ViewModel通常是有意义的,这样当您在它们之间切换时,数据就不必从存储库中提取,因此转换速度更快。如果其中任何一个都有大量的唯一数据,你可以考虑把它分解成一个单独的视图模型,这样当它不需要时,它就不会占用内存。 假设您使用的是共享ViewModel,根据您喜欢的代码样式,您可以使用至少两种不同的方法之一。在封装和代码复制之间有一个小的折衷,尽管它并没有真正封装,因为它们在看同一个实例。所以就我个人而言,我更喜欢第二种方式

  • 每个ViewModel直接创建ViewModel。如果通过activityViewModels()使用
    ,则ViewModel的作用域将限定为该活动,因此它们都将接收相同的实例。但由于ViewModel需要一个自定义工厂,因此必须在两个片段中都指定它,因此有一点代码重复:
  • 在MainActivity中指定ViewModel一次,并通过强制转换活动在片段中访问它
  • 或者,为了避免代码重复,您可以为片段创建扩展属性,这样它们就不必有这种代码重复:

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

    如果您两次初始化viewmodel,您将有两个单独的实例,我认为这与您声明的目的背道而驰。@KristyWelsh因此,此时,我应该像在示例中一样在活动中这样做,但是,一旦在活动中初始化,我如何在片段中使用它呢?是什么阻止您为每个片段使用不同类型的ViewModel?它们都可以从同一个位置观察数据源Repository@IvanWooll实际上,我在问在fragmentsOK中初始化两个ViewModel是否是一个好的实践,因此如果您的片段尝试首先实例化它,它没有使用自定义工厂,因此它失败了。我会写一个更详细的答案。最后使用了第二个解决方案,正如你在第一个解决方案中提到的那样,我无法使用application,即使是第三个解决方案也可以,谢谢。现在我遇到了“无法访问主线程上的数据库”的问题,但我会提出一个新问题。今天的那个问题是重复的,答案很差。看看Kotlin协程的答案,因为接受的答案已经过时了。我实际上一步一步地遵循,在这个示例中,数据插入没有问题,并且使用了协程。。