如何使用 Android 导航抽屉从另一个片段打开一个片段?

How to open a fragment from another fragment using the Android Navigation Drawer?

我有一个带有菜单的 Android 导航抽屉,点击项目打开片段。目前很好。 其中一些片段需要打开一个新片段以获取更多详细信息。我使用第一个片段中的这段代码来做到这一点:

getParentFragmentManager().beginTransaction().replace(R.id.nav_host_fragment_content_main, new MySecondFragment(), null).commit();

点击后退按钮(左上角的后退箭头)不会将我带回 MyFirstFragment。它通过一个例外:

java.lang.IllegalStateException: Fragment MyFirstFragment not associated with a fragment manager.

我可以使用其他代码:

getParentFragmentManager().beginTransaction().add(R.id.nav_host_fragment_content_main, new MySecondFragment(), null).commit();

但是使用这段代码,两个片段同时可见。我的意思是,显示了 MySecondFragment 视图,但我在后台看到了 MyFirstFragment 视图。

关于如何从 MyFirstFragment 打开 MySecondFragment 的任何想法,然后,如果按下后退箭头,我想 return 到 MyFirstFragment 之前所在的相同位置。

谢谢

But with this code both fragments are visible at the same time.

您可以将背景颜色设置为其根部layout.xml

您也可以使用fragmentTransaction.addToBackStack(null)将此事务添加到后台堆栈

新答案:

使用导航抽屉,片段事务似乎发生在 NavHostFragment 及其 FragmentTransactionManager 下。 这迫使我们获取它的 childFragmentManager() 并将其用于检查 backStackEntryCount.

因此,仅将嵌套片段添加到父后退堆栈是不够的。我们需要覆盖 onBackPressedonSupportNavigateUp 并在返回和弹出它时考虑 NavHostFragment 的后栈。

    private fun handleNestedFragmentsBackStack(): Boolean {
        val navHostChildFragmentManager = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment_content_main)?.childFragmentManager
        
        return if (navHostChildFragmentManager?.backStackEntryCount!! > 1) {
            navHostChildFragmentManager.popBackStack()
            false
        } else {
            val navController = findNavController(R.id.nav_host_fragment_content_main)
            navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
        }
    }

    override fun onBackPressed() {
        handleNestedFragmentsBackStack()
    }

    override fun onSupportNavigateUp(): Boolean {
        return handleNestedFragmentsBackStack()
    }

现在,将第二个片段添加到父级的后台堆栈时,请记住父级不是 Activity,而是 NavHostFragment。 您可以按照初始答案中的建议进行片段交易。

初始答案:

应该适用于标准片段交易

将您的片段添加到后台堆栈应该可以解决您的问题

    FragmentTransaction ft = getParentFragmentManager().beginTransaction()

    ft.replace(R.id.nav_host_fragment_content_main, new MySecondFragment(), null)
    ft.addToBackStack(MySecondFragment.class.getName()) // you can use a string here, using the class name is just convenient 
    ft.commit();

按下后退按钮时,您将浏览后退堆栈。

Kotlin 代码中相同:

    parentFragmentManager.commit {
        replace(R.id.nav_host_fragment_content_main, MySecondFragment())
        addToBackStack(MySecondFragment::class.java.name)
    }

解决方案在Java代码中:

在带有导航抽屉的 AppCompatActivity 中:

    @Override
    public boolean onSupportNavigateUp() {

        FragmentManager oChildFragmentManager = getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_content_main).getChildFragmentManager();
        if(oChildFragmentManager.getBackStackEntryCount() > 1){
            oChildFragmentManager.popBackStack();
            return true;
        }

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
        return NavigationUI.navigateUp(navController, mAppBarConfiguration) || super.onSupportNavigateUp();
    }


    @Override
    public void onBackPressed() {

        FragmentManager oChildFragmentManager = getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_content_main).getChildFragmentManager();
        if(oChildFragmentManager.getBackStackEntryCount() > 1){
            oChildFragmentManager.popBackStack();
            return;
        }

        super.onBackPressed();
    }

任何时候打开片段都不要忘记addToBackStack:

getParentFragmentManager().beginTransaction().replace(R.id.nav_host_fragment_content_main, new MyNewFragment()).addToBackStack("").commit();