如何处理 fragments 和 viewpager 之间的 backstack?

How to handle backstack between fragments and viewpagers?

我正在使用具有 2 个页面的 ViewPager。该 ViewPager 的第一页是 MainFragment。 MainFragment 的 ViewPager 为 bottomNavigationView。每个页面都有一个 FragmentContainerView,默认情况下它们分别包含 HomeFragment、CommunityFragment 和 NotificationFragment。

这是项目的Source Code and this is the APK。因此您可以轻松地对其进行测试和改进。

现在,如果我在 HomeFragment 中并单击配置文件按钮,它会处理到 ProfileFragment 并从那里设置等等。点击后退按钮,它会一个接一个地完美返回。但它不会与其他 FragmentContainerView 发生相同的情况。即使他们直接回到父片段。总的来说,我无法处理不同 ViewPagers 和片段之间的堆栈。

为了避免FragmentContainers的混淆,我是这样处理的

val containerId = (view?.parent as ViewGroup).id
activity?.supportFragmentManager?.beginTransaction()?.add(containerId, profileFragment)?.addToBackStack(null)?.commit()

现在MainActivity中BackPressed()的处理就在这里

if (view_pager_main_activity?.currentItem == 0)
{
    if (view_pager_main_fragment?.currentItem == 0)
    {
        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view_home)
        val appbarHome = findViewById<AppBarLayout>(R.id.appbar_home)
        val layoutManager = recyclerView?.layoutManager as LinearLayoutManager
        when {
            layoutManager.findFirstCompletelyVisibleItemPosition() == 0 -> {
                super.onBackPressed()
            }
            supportFragmentManager.backStackEntryCount != 0 -> {
                supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
            }
            else -> {
                layoutManager.scrollToPositionWithOffset(0, 0)
                appbarHome.setExpanded(true)
                //recyclerView.smoothScrollToPosition(0)
            }
        }
    }
    else
    {
        // Otherwise, select the previous step.
        view_pager_main_fragment?.setCurrentItem(view_pager_main_fragment.currentItem - 1, false)
    }
}
else
{
    // Otherwise, select the previous step.
    view_pager_main_activity?.currentItem = view_pager_main_activity.currentItem - 1
}

我检查了你的代码,问题是

的用法
supportFragmentManager.popBackStack(null,FragmentManager.POP_BACK_STACK_INCLUSIVE)

如果您阅读标志 POP_BACK_STACK_INCLUSIVE 的描述,它会说:

If set, and the name or ID of a back stack entry has been supplied, then all matching entries will be consumed until one that doesn't match is found or the bottom of the stack is reached. Otherwise, all entries up to but not including that entry will be removed.

所以它一次删除了多个条目。

它适用于主屏幕的原因是它从未达到那种情况。它总是出现在第一种情况下:

layoutManager.findFirstCompletelyVisibleItemPosition() == 0 -> {
                        super.onBackPressed()
                    }  

这正是您的案例所需要的,当您只想一次弹出一个片段时,只需调用 super.onBackPressed() 即可。所以你的最终代码变成这样:

在您的 MainActivity 的 onBackPressed()

override fun onBackPressed() {
        if (view_pager_main_activity?.currentItem == 0) {
            if (view_pager_main_fragment?.currentItem == 0) {
                val recyclerView = findViewById<RecyclerView>(R.id.listRecyclerView)
                val appbarHome = findViewById<AppBarLayout>(R.id.appbar_home)
                val layoutManager = recyclerView?.layoutManager as LinearLayoutManager
                when {
                    layoutManager.findFirstCompletelyVisibleItemPosition() == 0 -> {
                        super.onBackPressed()
                    }
                    //This is never reached. It can be removed
                    supportFragmentManager.backStackEntryCount != 0 -> {
                        supportFragmentManager.popBackStack(
                            null,
                            FragmentManager.POP_BACK_STACK_INCLUSIVE
                        )
                    }
                    else -> {
                        layoutManager.scrollToPositionWithOffset(0, 0)
                        appbarHome.setExpanded(true)
                        //recyclerView.smoothScrollToPosition(0)
                    }
                }
            } else if (supportFragmentManager.backStackEntryCount != 0) {
                super.onBackPressed()
            } else {
                // Otherwise, select the previous step.
                view_pager_main_fragment?.setCurrentItem(
                    view_pager_main_fragment.currentItem - 1,
                    false
                )
            }
        } else if (supportFragmentManager.backStackEntryCount != 0) {
            super.onBackPressed()
        } else {
            // Otherwise, select the previous step.
            view_pager_main_activity?.currentItem = view_pager_main_activity.currentItem - 1
        }
    } 

编辑 1:来自评论

I want to clear the backstack when bottom nav selection changes.

如果您想在更改底部导航选择更改时清除选择,则必须设置监听器并清除 MainFragment 中的返回堆栈,如下所示:

view?.bottomNavView?.setOnNavigationItemSelectedListener { item ->
            //check if item is being re-selected then just return and don't do anything
            if (bottomNavView.selectedItemId == item.itemId){
                return@setOnNavigationItemSelectedListener false
            }
            //if the bottom nav item selection changes then clear the back stack of previous tab
            parentFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)

            when (item.itemId) {
                R.id.homeFragment -> viewPager2.setCurrentItem(0, false)
                R.id.searchFragment -> viewPager2.setCurrentItem(1, false)
                R.id.notificationsFragment -> viewPager2.setCurrentItem(2, false)
            }
            true
        }  

其余的可以保持不变。试试看,一定要告诉我。