ViewPager2如何在返回时恢复片段

发布于 2025-01-13 16:16:02 字数 4028 浏览 0 评论 0原文

大家好,我有一个具有单一活动架构的 ViewPager2 。当我单击某个按钮时,我会使用 Jetpack 导航库将 ViewPager2 主机片段替换为另一个主机片段。

这会调用主机片段的onDestroyView。当我点击返回时,我们回到了onCreateView。我怎样才能返回到我正在查看的 ViewPager2 ,因为主机片段本身没有被破坏?

我相信根据这个答案,恢复ViewPager2实际上是不可能的,不确定这是否是无论是否有意设计。那么这里的最佳实践是什么,假设每个片段加载一个沉重的列表,每次用户将后退堆栈弹出到我的视图分页器中时,我是否应该重新加载所有数据?我唯一能想到的就是拥有一个活动范围的 ViewModel,它维护每个片段的数据列表,这听起来很荒谬,想象一下,如果我的页面是动态生成的,或者我在每个页面上都有多个回收器视图这

是我的尝试,我试图在返回时做最少的事情,但是如果没有再次分配视图寻呼机适配器,我正在查看一个空白的片段选项卡。我不明白这一点,绑定还没有消失,那么为什么视图分页器无法恢复我的片段?

OrderTabsFragment.kt

var adapter: TabsPagerAdapter? = null
private var _binding: FragmentOrdersTabsBinding? = null
private val binding get() = _binding!!
private var initted = false

override fun onCreate(savedInstanceState: Bundle?) {
    Timber.d("OrderTabsFragment $initted - onCreate $savedInstanceState")
    super.onCreate(savedInstanceState)

    adapter = TabsPagerAdapter(this, Tabs.values().size)
    adapter?.currentTab = Tabs.valueOf(savedInstanceState?.getString(CURRENT_TAB) ?: Tabs.ACTIVE.name)
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    Timber.d("OrderTabsFragment $initted - onCreateView $savedInstanceState, _binding=$_binding")
    if(_binding == null)
        _binding = FragmentOrdersTabsBinding.inflate(inflater, container, false)

    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    Timber.d("OrderTabsFragment $initted - onViewCreated $savedInstanceState")

    super.onViewCreated(view, savedInstanceState)

    if(!initted) {
        initted = true

        val viewpager = binding.viewpager
        viewpager.adapter = adapter
        viewpager.isSaveEnabled = false

        binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab?) {}
            override fun onTabUnselected(tab: TabLayout.Tab?) {}
            override fun onTabReselected(tab: TabLayout.Tab?) {
                if (adapter?.currentTab == Tabs.FILTERED) {
                    showFilterBalloon(tab)
                }
            }
        })
        TabLayoutMediator(binding.tabLayout, viewpager) { tab, position ->
            when (position) {
                0 -> tab.text = getString(R.string.title_active).uppercase(Locale.getDefault())
                1 -> tab.text =
                    getString(R.string.title_scheduled).uppercase(Locale.getDefault())
                2 -> tab.text =
                    getString(R.string.title_complete).uppercase(Locale.getDefault())
            }
        }.attach()
    }
    else{
        val viewpager = binding.viewpager
        viewpager.adapter = adapter //Required otherwise we are looking at a blank fragment tab. The adapter rv was detached and can't be reattached?
        viewpager.isSaveEnabled = false //Required otherwise "Expected the adapter to be 'fresh' while restoring state."
    }
}

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    Timber.d("OrderTabsFragment $initted - onSaveInstanceState")
    outState.putString(CURRENT_TAB, adapter?.currentTab?.name)
}


override fun onDestroy() {
    super.onDestroy()
    Timber.d("OrderTabsFragment $initted - onDestroy")
    binding.viewpager.adapter = null
    _binding = null
    adapter = null
}

enum class Tabs {
    ACTIVE, SCHEDULED, COMPLETE, FILTERED
}

编辑: 其他地方也出现了大致相同的问题1,< a href="https://github.com/android/views-widgets-samples/issues/139" rel="nofollow noreferrer">2、3

Hi folks I have a ViewPager2 with single activity architecture. When I click a button, I swap out the ViewPager2 host fragment with another one using the Jetpack Navigation library.

This calls onDestroyView for the host fragment. When I click back, we are back to onCreateView. How can I return to the ViewPager2 I was looking at, seeing as the host fragment itself is not destroyed?

I believe based on this answer that restoring a ViewPager2 is actually impossible, not sure if this is by design or not. So what is the best practice here, assuming each fragment loads a heavy list, am I supposed to reload all the data every time a user pops the backstack into my viewpager? The only thing I can think of is to have an activity scoped ViewModel which maintains the list of data for each fragment, which sounds ridiculous, imagine if my pages were dynamically generated or I had several recycler views on each fragment....

Here is my attempt, I am trying to do the bare minimum when navigating back, however without assigning the view pager adapter again, I am looking at a blank fragment tab. I don't understand this, the binding has not died, so why is the view pager not capable of restoring my fragment?

OrderTabsFragment.kt

var adapter: TabsPagerAdapter? = null
private var _binding: FragmentOrdersTabsBinding? = null
private val binding get() = _binding!!
private var initted = false

override fun onCreate(savedInstanceState: Bundle?) {
    Timber.d("OrderTabsFragment $initted - onCreate $savedInstanceState")
    super.onCreate(savedInstanceState)

    adapter = TabsPagerAdapter(this, Tabs.values().size)
    adapter?.currentTab = Tabs.valueOf(savedInstanceState?.getString(CURRENT_TAB) ?: Tabs.ACTIVE.name)
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    Timber.d("OrderTabsFragment $initted - onCreateView $savedInstanceState, _binding=$_binding")
    if(_binding == null)
        _binding = FragmentOrdersTabsBinding.inflate(inflater, container, false)

    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    Timber.d("OrderTabsFragment $initted - onViewCreated $savedInstanceState")

    super.onViewCreated(view, savedInstanceState)

    if(!initted) {
        initted = true

        val viewpager = binding.viewpager
        viewpager.adapter = adapter
        viewpager.isSaveEnabled = false

        binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab?) {}
            override fun onTabUnselected(tab: TabLayout.Tab?) {}
            override fun onTabReselected(tab: TabLayout.Tab?) {
                if (adapter?.currentTab == Tabs.FILTERED) {
                    showFilterBalloon(tab)
                }
            }
        })
        TabLayoutMediator(binding.tabLayout, viewpager) { tab, position ->
            when (position) {
                0 -> tab.text = getString(R.string.title_active).uppercase(Locale.getDefault())
                1 -> tab.text =
                    getString(R.string.title_scheduled).uppercase(Locale.getDefault())
                2 -> tab.text =
                    getString(R.string.title_complete).uppercase(Locale.getDefault())
            }
        }.attach()
    }
    else{
        val viewpager = binding.viewpager
        viewpager.adapter = adapter //Required otherwise we are looking at a blank fragment tab. The adapter rv was detached and can't be reattached?
        viewpager.isSaveEnabled = false //Required otherwise "Expected the adapter to be 'fresh' while restoring state."
    }
}

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    Timber.d("OrderTabsFragment $initted - onSaveInstanceState")
    outState.putString(CURRENT_TAB, adapter?.currentTab?.name)
}


override fun onDestroy() {
    super.onDestroy()
    Timber.d("OrderTabsFragment $initted - onDestroy")
    binding.viewpager.adapter = null
    _binding = null
    adapter = null
}

enum class Tabs {
    ACTIVE, SCHEDULED, COMPLETE, FILTERED
}

Edit:
Here's roughly the same questions coming up in other places 1, 2, 3

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文