【问题标题】:Android Navigation not working properly with shared view model mvvmAndroid Navigation 无法与共享视图模型 mvvm 一起正常工作
【发布时间】:2021-04-02 18:47:55
【问题描述】:

我有两个共享相同视图模型的片段

ShoeListFragment 显示带有 fab 的 sheos 列表,ShoeDetailsFragment 让用户在 fab 点击时添加新鞋

ShoeDetailsFragment 用户可以点击添加或取消。添加后,鞋子将被添加并显示在ShoeListFragment

如果用户点击取消,我们会将用户导航至ShoeListFragment

现在在第一次运行中,我尝试在 fab 中添加新鞋,点击它被添加并显示。 在第二次运行中,我尝试了取消按钮,然后导航到 ShoeListFragment

但是,如果在同一次运行中我尝试添加,然后导航到 ShoeListFragment,我点击 fab,它被点击,ShoeDetailsFragment 被调用,但布局不可见!

我的意思是 ShoeListFragment 布局仅可见。

ShoeListFragment

class ShoeListFragment : Fragment() {

lateinit var viewModel : ShoeListViewModel
lateinit var binding : FragmentShoeListBinding
lateinit var parentLayout: LinearLayout

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    binding = DataBindingUtil.inflate(inflater , R.layout.fragment_shoe_list , container , false)

    binding.fabAddShoe.setOnClickListener({
        Timber.i("fab clicked")
        findNavController().navigate(ShoeListFragmentDirections.actionShoesFragmentToShoeDetailFragment())
    })

    viewModel = ViewModelProvider(requireActivity()).get(ShoeListViewModel::class.java)

    Timber.i(viewModel.mShoeList.size.toString())

    viewModel.shoeList.observe(viewLifecycleOwner , Observer { shoeList ->
        Timber.i("List shoe observer")
        displayList(shoeList)
    })

    parentLayout = binding.llParent
    setHasOptionsMenu(true)

    // Inflate the layout for this fragment
    return binding.root
}


override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
    Timber.i("onCreateOptionsMenu")
    inflater.inflate(R.menu.main_menu, menu)
    super.onCreateOptionsMenu(menu, inflater)
}

// Menu item id must match the destination id to navigate there
override fun onOptionsItemSelected(item: MenuItem): Boolean {
    Timber.i("onOptionsItemSelected")
    return NavigationUI.onNavDestinationSelected(item, requireView().findNavController())
            || super.onOptionsItemSelected(item)
}
}

ShoeDetailFragment

class ShoeDetailFragment : Fragment() {

lateinit var binding : FragmentShoeDetailBinding
lateinit var viewModel : ShoeListViewModel

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    // View binding
    binding = DataBindingUtil.inflate(inflater,R.layout.fragment_shoe_detail , container , false)

    // Data binding - shared view model
    viewModel = ViewModelProvider(requireActivity()).get(ShoeListViewModel::class.java)
    binding.shoeListViewModel = viewModel

    binding.shoeModel = Shoe("" , 0.0 , "" , "")

    //Observers
    viewModel.isCancelAdd.observe(viewLifecycleOwner , Observer { isCancel ->
        Timber.i("Fragment Cancel shoe")
        findNavController().navigate(ShoeDetailFragmentDirections.actionShoeDetailFragmentToShoesFragment())
    })

    viewModel.isAddShoe.observe(viewLifecycleOwner , Observer { isAdd ->
       findNavController().navigate(ShoeDetailFragmentDirections.actionShoeDetailFragmentToShoesFragment())
    })

    // Inflate the layout for this fragment
    return binding.root
}

共享视图模型

class ShoeListViewModel : ViewModel() {

/* Should we make shoeList observing mShoeList instead of doing
shoeList.value = mShoeList in once we add new shoe?
*/

private var _shoeList = MutableLiveData<MutableList<Shoe>>()
val shoeList : LiveData<MutableList<Shoe>>
    get() = _shoeList

private var _isCancelAdd = MutableLiveData<Boolean>()
val isCancelAdd : LiveData<Boolean>
    get() = _isCancelAdd

private var _isAddShoe = MutableLiveData<Boolean> ()
val isAddShoe : LiveData<Boolean>
    get() = _isAddShoe

var mShoeList : MutableList<Shoe>



init {
    mShoeList = mutableListOf(
        Shoe("Filla" , 40.0 , "Filla" , "Filla shoes") ,
        Shoe("Addidad" , 30.0 , "Addidas" , "Addidas shoes")
    )
    _shoeList.value = mShoeList
}

fun addShoe (shoe : Shoe) {
    _isAddShoe.value = true
    mShoeList.add(shoe)
    _shoeList.value = mShoeList
    Timber.i("In Add shoe")
}

fun cancelAddShoe () {
    _isCancelAdd.value = true
    Timber.i("In Cancel shoe")
}

我试过调试它,第二次这条线在shoeDetailsFragment中执行

 binding = DataBindingUtil.inflate(inflater,R.layout.fragment_shoe_detail , container , false)

直到到达

 return binding.root

然后这被调用了

binding = DataBindingUtil.inflate(inflater , R.layout.fragment_shoe_list , container , false)

这就是为什么我认为鞋子细节片段不可见的原因。 但是为什么会发生这种行为呢?

【问题讨论】:

    标签: android kotlin mvvm navigation viewmodel


    【解决方案1】:

    问题是,您的 isCancelAdd 值将被缓存,因此在第二次导航后您将立即获得该事件,并且您将从详细信息返回。

    通常对于事件,您有两种选择:

    1. 使用SingleLiveEvent 模式或Consumable,以确保事件不会被多次使用。有关更多信息,请参阅此博客:https://proandroiddev.com/singleliveevent-to-help-you-work-with-livedata-and-events-5ac519989c70

    2. 使用SharedFlows。我会这样做,因为现在这是推荐的方式,LiveDatas 正在被弃用

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-18
      • 1970-01-01
      • 2012-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多