【问题标题】:ViewPager2/Tabs problem with ViewModel stateViewPager2/Tabs 问题与 ViewModel 状态
【发布时间】:2020-05-24 12:26:56
【问题描述】:

我遵循 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() 离开那个片段然后返回那个片段时,我会得到一个新的空视图模型。 这是故意的。

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


更新 #1

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


更新 #2

我这样设置我的 ViewModel:

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

我得到了相同的结果:

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

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

【问题讨论】:

  • 您能展示一下您是如何设置 ViewModel 的吗?另外,你有什么理由不能在获取新数据时创建一个新的 ViewModel 吗?
  • 我已经更新了我的问题。视图模型的重点不是它自己处理吗?我创建它一次,它会持续存在一个片段。如果我有新数据,我将如何重新创建它?为什么我以前不需要这样做?
  • 您之前不需要这样做,因为片段已被破坏。现在您正在使用 ViewPager 并将片段存储在内存中。我建议您在需要时清除数据。您需要管理 VM 中的数据,而不是 VM 本身。
  • 我遇到的问题是,旧的虚拟机实际上仍在为旧的 LiveData 提供服务,并为其他组件提供旧数据。所以清除数据是行不通的,因为旧的虚拟机一直在干扰。示例:我清除了当前 ViewModel 中的一个列表。但是,屏幕仍然会获取旧列表。当我调试 ViewModel 并检查 List 的长度时,它显示为 0 - 因为它已被清除。唯一合乎逻辑的解释是其他提供旧数据的 ViewModel。
  • 您是否对每个片段使用相同的虚拟机?还是每个片段都有自己的虚拟机?

标签: java android android-viewmodel android-architecture-navigation android-viewpager2


【解决方案1】:

根据您的评论,您正在使用 Fragment 并且在该 Fragment 内部有您的浏览器。 因此,在为 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)

我希望这能解决您的问题,因为我有一天也遇到了同样的问题。

编码愉快。

【讨论】:

  • 谢谢。正如 ianhanniballake 已经说过的,传入片段本身就足够了,只要确保你有一个合适的构造函数。所以两个答案都是正确的。
【解决方案2】:
new ViewModelProvider(requireActivity()).get("your_key", YourViewModel.class)

使用 requireActivity 获取 ViewModel 而不是片段。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-24
    • 2020-11-30
    • 1970-01-01
    • 2019-05-25
    • 1970-01-01
    • 2021-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多