【问题标题】:Shared ViewModel Not Working With Bottom Sheet Dialog Fragment, DB and UI共享 ViewModel 不适用于底部工作表对话框片段、数据库和用户界面
【发布时间】:2022-11-25 02:20:56
【问题描述】:

我有一个非常简单的词汇笔记应用程序,包含 2 个片段和 1 个根活动。在 HomeFragment 中我有一个按钮“addVocabularyButton”。单击它时,会出现 BottomSheetDialogFragment 并且用户提供 3 个输入,并使用视图模型将其保存在数据库中。我的问题是当我将输入保存到数据库时它工作正常但我无法立即在 HomeFragment 中看到那个词。我必须重新运行该应用程序才能在主页片段中查看。我在主页片段中使用导航库和回收器视图。

Github 链接:https://github.com/ugursnr/MyVocabularyNotebook

首页片段

class HomeFragment : Fragment() {
    private var _binding : FragmentHomeBinding? = null
    private val binding get() = _binding!!

    private var vocabularyAdapter = VocabulariesHomeAdapter()
    private lateinit var sharedViewModel: AddVocabularySharedViewModel
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentHomeBinding.inflate(layoutInflater,container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        //sharedViewModel = ViewModelProvider(this)[AddVocabularySharedViewModel::class.java]
        sharedViewModel = (activity as MainActivity).sharedViewModel
        sharedViewModel.getAllVocabulariesFromDB()
        observeAllVocabularies()
        prepareRecyclerView()
        addVocabularyOnClick()

        vocabularyAdapter.onItemDeleteClicked = {
            sharedViewModel.deleteVocabulary(it)
            observeAllVocabularies()

        }

    }


    private fun prepareRecyclerView(){

        binding.recyclerViewHome.apply {
            layoutManager = LinearLayoutManager(context)
            adapter = vocabularyAdapter
        }
    }

    private fun addVocabularyOnClick(){
        binding.addVocabularyButton.setOnClickListener{
            val action = HomeFragmentDirections.actionHomeFragmentToAddVocabularyBottomSheetFragment()
            Navigation.findNavController(it).navigate(action)
        }
    }

    private fun observeAllVocabularies(){

        sharedViewModel.allVocabulariesLiveData.observe(viewLifecycleOwner, Observer {

            vocabularyAdapter.updateVocabularyList(it)

        })
    }

}

对话片段

class AddVocabularyBottomSheetFragment : BottomSheetDialogFragment() {
    private var _binding : FragmentAddVocabularyBottomSheetBinding? = null
    private val binding get() = _binding!!
    private lateinit var sharedViewModel: AddVocabularySharedViewModel

    private var vocabularyInput : String? = null
    private var translationInput : String? = null
    private var sampleSentenceInput : String? = null

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentAddVocabularyBottomSheetBinding.inflate(layoutInflater,container,false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        //sharedViewModel = ViewModelProvider(this)[AddVocabularySharedViewModel::class.java]
        sharedViewModel = (activity as MainActivity).sharedViewModel
        binding.addOrUpdateVocabularyButton.setOnClickListener {

            vocabularyInput = binding.vocabularyActualET.text.toString()
            translationInput = binding.vocabularyTranslationET.text.toString()
            sampleSentenceInput = binding.vocabularySampleSentenceET.text.toString()

            val inputVocabulary = Vocabulary(vocabularyInput,translationInput,sampleSentenceInput)
            insertVocabularyToDB(inputVocabulary)
            sharedViewModel.getAllVocabulariesFromDB()
            dismiss()

        }

    }

    private fun insertVocabularyToDB(vocabulary: Vocabulary){
        sharedViewModel.insertVocabulary(vocabulary)
    }

    
}

共享视图模型

class AddVocabularySharedViewModel(application: Application) : AndroidViewModel(application) {

    private var _allVocabulariesLiveData = MutableLiveData<List<Vocabulary>>()
    private var _vocabularyLiveData = MutableLiveData<Vocabulary>()

    val allVocabulariesLiveData get() = _allVocabulariesLiveData
    val vocabularyLiveData get() = _vocabularyLiveData

    val dao = VocabularyDatabase.makeDatabase(application).vocabularyDao()
    val repository = VocabularyRepository(dao)

    fun insertVocabulary(vocabulary: Vocabulary) = CoroutineScope(Dispatchers.IO).launch {
        repository.insertVocabulary(vocabulary)
    }
    fun updateVocabulary(vocabulary: Vocabulary) = CoroutineScope(Dispatchers.IO).launch {
        repository.updateVocabulary(vocabulary)
    }
    fun deleteVocabulary(vocabulary: Vocabulary) = CoroutineScope(Dispatchers.IO).launch {
        repository.deleteVocabulary(vocabulary)
    }

    fun getAllVocabulariesFromDB() = CoroutineScope(Dispatchers.IO).launch {
        val temp = repository.getAllVocabulariesFromDB()

        withContext(Dispatchers.Main){
            _allVocabulariesLiveData.value = temp
        }

    }

    fun getVocabularyDetailsByID(vocabularyID : Int) = CoroutineScope(Dispatchers.IO).launch {

        val temp = repository.getVocabularyDetailsByID(vocabularyID).first()

        withContext(Dispatchers.Main){
            _vocabularyLiveData.value = temp
        }

    }
    

}

适配器

class VocabulariesHomeAdapter : RecyclerView.Adapter<VocabulariesHomeAdapter.VocabulariesHomeViewHolder>() {

    lateinit var onItemDeleteClicked : ((Vocabulary) -> Unit)


    val allVocabulariesList = arrayListOf<Vocabulary>()
    class VocabulariesHomeViewHolder(val binding : RecyclerRowBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VocabulariesHomeViewHolder {
        return VocabulariesHomeViewHolder(RecyclerRowBinding.inflate(LayoutInflater.from(parent.context), parent, false))
    }

    override fun onBindViewHolder(holder: VocabulariesHomeViewHolder, position: Int) {
        val vocabulary = allVocabulariesList[position]
        holder.binding.apply {
            actualWordTV.text = vocabulary.vocabulary
            translationWordTV.text = vocabulary.vocabularyTranslation

            deleteButtonRV.setOnClickListener {
                onItemDeleteClicked.invoke(vocabulary)
                notifyItemRemoved(position)
            }
        }



    }

    override fun getItemCount(): Int {
        return allVocabulariesList.size
    }

    fun updateVocabularyList(newList : List<Vocabulary>){
        allVocabulariesList.clear()
        allVocabulariesList.addAll(newList)
        notifyDataSetChanged()
    }


}

我知道上面有很多代码,但我在使用这些对话框片段时遇到了很大的问题。谢谢您的帮助。

【问题讨论】:

    标签: android kotlin android-dialogfragment


    【解决方案1】:

    这是因为导航库为每个导航屏幕创建了相同视图模型的多个实例。

    您需要告诉导航库在所有导航屏幕之间共享相同的 ViewModel。

    您可以通过将 viewModel 的范围限定到导航图来完成此操作。

    你在使用任何依赖注入库吗?

    如果你使用 hilt,你可以将你的 NavGraph 传递给 hilt AndroidViewModel()

    val viewModel = hiltAndroidViewModel(//pass NavGraph)
    

    这将为您提供一个范围为 NavBackStackEntry 的 viewModel,并且只会在您弹出 NavBackStackEntry(即导航出导航屏幕)时重新创建。

    【讨论】:

      猜你喜欢
      • 2020-08-02
      • 2020-05-15
      • 2018-09-22
      • 1970-01-01
      • 2020-02-28
      • 1970-01-01
      • 2015-04-15
      • 1970-01-01
      • 2013-10-29
      相关资源
      最近更新 更多