You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Sheet内实现导航且无需两个NavController与NavHost

单个NavHost实现Sheet嵌套界面导航方案

核心思路:将Sheet及其内部界面纳入同一导航图分支

把Sheet根页面、内部所有界面都加入主导航图,通过目的地弹出规则和自定义导航动画区分主界面与Sheet层级,同时保证Sheet内跳转动画的一致性。

步骤1:在导航图中定义Sheet相关目的地

在nav_graph.xml里,给Sheet根目的地设置launchSingleTop="true",并为Sheet内跳转配置专属动画:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/main_home">

    <!-- 主界面分支 -->
    <fragment
        android:id="@+id/main_home"
        android:name="com.example.MainHomeFragment"
        android:label="Home">
        <action
            android:id="@+id/action_home_to_sheet_root"
            app:destination="@id/sheet_root"
            app:launchSingleTop="true"
            app:enterAnim="@anim/sheet_slide_up"
            app:exitAnim="@anim/fade_out"
            app:popEnterAnim="@anim/fade_in"
            app:popExitAnim="@anim/sheet_slide_down"/>
    </fragment>

    <!-- Sheet分支:根页面 -->
    <fragment
        android:id="@+id/sheet_root"
        android:name="com.example.SheetRootFragment"
        android:label="SheetRoot">
        <action
            android:id="@+id/action_sheet_root_to_sheet_detail1"
            app:destination="@id/sheet_detail1"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right"/>
        <action
            android:id="@+id/action_sheet_root_to_sheet_detail2"
            app:destination="@id/sheet_detail2"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right"/>
    </fragment>

    <!-- Sheet内部界面 -->
    <fragment
        android:id="@+id/sheet_detail1"
        android:name="com.example.SheetDetail1Fragment"
        android:label="SheetDetail1"/>
    <fragment
        android:id="@+id/sheet_detail2"
        android:name="com.example.SheetDetail2Fragment"
        android:label="SheetDetail2"/>
</navigation>

步骤2:监听导航变化控制Sheet显示

在主Activity中通过OnDestinationChangedListener监听页面跳转,同步Sheet的显示/隐藏状态:

class MainActivity : AppCompatActivity() {
    private lateinit var navController: NavController
    private var sheetRootFragment: SheetRootFragment? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        navController = findNavController(R.id.nav_host_fragment)
        navController.addOnDestinationChangedListener { _, destination, _ ->
            when (destination.id) {
                R.id.sheet_root, R.id.sheet_detail1, R.id.sheet_detail2 -> {
                    if (sheetRootFragment == null) {
                        sheetRootFragment = SheetRootFragment()
                        supportFragmentManager.beginTransaction()
                            .add(R.id.sheet_container, sheetRootFragment!!)
                            .commitNow()
                    }
                    sheetRootFragment?.showSheet()
                }
                else -> {
                    sheetRootFragment?.hideSheet {
                        supportFragmentManager.beginTransaction()
                            .remove(sheetRootFragment!!)
                            .commitNow()
                        sheetRootFragment = null
                    }
                }
            }
        }
    }

    override fun onBackPressed() {
        // 优先处理Sheet内部回退
        val sheetNavHost = sheetRootFragment?.childFragmentManager?.findFragmentById(R.id.sheet_nav_host) as? NavHostFragment
        if (sheetNavHost?.navController?.popBackStack() != true) {
            // Sheet无回退项时,关闭Sheet
            navController.popBackStack()
        } else {
            super.onBackPressed()
        }
    }
}

步骤3:Sheet内部嵌入子NavHost(简化管理)

若Sheet内界面较多,可在SheetRootFragment中嵌入子NavHost,让Sheet内跳转逻辑独立但仍属于主导航分支:

<!-- sheet_root_fragment.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/sheet_nav_host"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:navGraph="@navigation/sheet_nav_graph"/>
</androidx.constraintlayout.widget.ConstraintLayout>

关键注意事项

  • 动画一致性:Sheet弹出/隐藏用上下滑动动画,内部跳转统一用横向滑动,通过导航图的action配置实现。
  • 回退栈优先级:重写onBackPressed,先处理Sheet内部回退,再处理主界面回退。
  • 状态同步:Sheet的显示状态与导航目的地绑定,避免页面重建时状态错乱。

内容的提问来源于stack exchange,提问作者Hazem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 07:16:08