ViewModel状态的ViewPager2 / Tabs问题


9

我正在遵循MVVM模式-意味着每个片段都有一个ViewModel。

我使用ViewPager2 添加了两个选项卡。

我的适配器如下所示:

@Override
public Fragment createFragment(int position) {
    switch (position) {
        case 0:
            return new MergedItemsFragment();
        case 1:     
            return new ValidatedMergedItemsFragment();
    }
    return new MergedItemsFragment();
}

选项卡正在工作。但是,我注意到我的MergedItemsFragment的ViewModel行为异常。在添加选项卡之前,我像这样导航到片段:

NavHostFragment.findNavController(this).navigate(R.id.action_roomFragment_to_itemsFragment);

当我将该片段留给NavHostFragment.findNavController(this).popBackStack()以后再返回该片段时,我将得到一个新的空ViewModel。这是有意的。

使用新方法,我正在使用return new MergedItemsFragment()。当我离开该片段并稍后返回时,我得到一个包含旧数据的ViewModel 。这是一个问题,因为旧数据不再相关,因为用户在另一个片段中选择了不同的数据。


更新#1

我意识到他实际上将所有旧的Fragments保留在内存中,因为同一打印语句被多次调用。它的调用时间随着我离开并返回该屏幕的次数而增加。因此,如果我离开并返回10次并旋转设备,他实际上将执行10次一行。有人猜测如何以与ViewModels一起使用的方式来实现带有导航组件的Tabs / ViewPagers吗?


更新#2

我将ViewModels设置如下:

viewModel = new ViewModelProvider(this, providerFactory).get(MergedItemViewModel.class)

我得到相同的结果:

viewModel = ViewModelProviders.of(this).get(MergedItemViewModel.class);

我将ViewModel绑定在Fragment本身中。因此,this就是碎片。


您可以显示如何设置ViewModels吗?另外,有什么理由不能在获取新数据时仅创建新的ViewModel?
BlackHatSamurai

我已经更新了我的问题。视图模型不是要照顾自己吗?我创建了一次,它持续存在一个片段。如果我有新数据,我将如何精确地重新创建它?为什么以前不需要这样做?
user123456789

您不需要这样做,因为片段已被破坏。现在,您正在使用ViewPager,它将片段存储在内存中。我建议您仅在需要时清除数据。您需要管理VM中的数据,而不是VM本身。
BlackHatSamurai

我的问题是,旧的VM实际上仍在提供旧的LiveData并向其他组件提供旧数据。因此清除数据将不起作用,因为旧的VM一直在干扰。示例:我清除当前ViewModel中的列表。但是,屏幕仍然会显示旧列表。当我调试ViewModel并检查List的长度时,它显示为0-因为它已被清除。唯一合乎逻辑的解释是提供旧数据的其他ViewModel。
user123456789

您是否为每个片段使用相同的VM?还是每个片段都有自己的虚拟机?
BlackHatSamurai

Answers:


3

根据您的评论,您正在使用Fragment,并且在Fragment中有您的viewpager。因此,在为ViewPager创建适配器时,您需要传递childFragmentManager而不是getActivity()

以下是您可以使用的viewPager适配器示例

class NewViewPagerAdapter(fm: FragmentManager, behavior: Int) : FragmentStatePagerAdapter(fm, behavior) {
    private val mFragmentList: MutableList<Fragment> = ArrayList()
    private val mFragmentTitleList: MutableList<String> = ArrayList()

    override fun getItem(position: Int): Fragment {
        return mFragmentList[position]
    }

    override fun getCount(): Int {
        return mFragmentList.size
    }

    fun addFragment(fragment: Fragment, title: String) {
        mFragmentList.add(fragment)
        mFragmentTitleList.add(title)
    }

    override fun getPageTitle(position: Int): CharSequence? {
        return mFragmentTitleList[position]
    }
}

并且在创建适配器时将其命名为

   val adapter = NewViewPagerAdapter(
        childFragmentManager,
        FragmentPagerAdapter.POSITION_UNCHANGED
    )

就像您看到FragmentStatePagerAdapter的文档一样,它表明您应该在适配器的构造函数中传递(FragmentManager,int)

我希望这能解决您的问题,因为有一天我正面临着同样的问题。

快乐的编码。


1
谢谢。正如ianhanniballake所说的那样,传递片段本身就足够了,只需确保您有合适的构造器即可。因此,两个答案都是正确的。
user123456789
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.