Kotlin 实现Navigation Drawer菜单项等效程序化导航
问题原因
你当前使用的action_nav_home_to_nav_maps是定义在Home Fragment节点下的专属跳转动作,执行这类跳转时导航组件会默认将Maps Fragment压入当前返回栈的Home Fragment上层,不会做栈清理和实例复用,自然会出现返回时叠层、重复创建同类型Fragment的问题,和导航抽屉的菜单点击逻辑本质上不是一套机制。
导航抽屉的菜单项点击本质是顶层目的地切换,默认会自动清理同层级栈内实例、复用已存在的目标Fragment、保留各页面的状态,不会产生堆叠。
正确实现代码
首先确认两个前置配置(Android Studio默认创建的Navigation Drawer模板已经自动配置,无需额外修改):
- 导航图中
nav_home、nav_maps都属于顶层目的地 - 导航抽屉对应的menu文件中,Maps菜单项的id和导航图中
nav_maps的id完全一致
在Home Fragment的按钮点击事件中使用如下代码,即可和点击抽屉菜单项实现完全一致的跳转效果:
btnJumpToMaps.setOnClickListener { val navController = findNavController() navController.navigate(R.id.nav_maps) { // 弹出到导航图起始目的地,避免栈内堆叠多个顶层Fragment popUpTo(navController.graph.startDestinationId) { saveState = true } // 目标目的地如果已在栈顶则直接复用,不重复创建实例 launchSingleTop = true // 切换时自动恢复目标页面之前保存的状态 restoreState = true } }
可选简化方案
如果项目里已经引入了NavigationUI依赖,也可以直接调用NavigationUI内置的菜单选择逻辑,完全复用抽屉点击的原生逻辑,不用自己手写导航参数:
btnJumpToMaps.setOnClickListener { val navController = findNavController() // 构造和抽屉中id一致的菜单项 val mapsMenuItem = NavigationBarMenu(requireContext()).apply { add(R.id.nav_maps) }.getItem(0) // 直接调用NavigationUI的导航逻辑,和抽屉点击走完全相同的执行链路 NavigationUI.onNavDestinationSelected(mapsMenuItem, navController) }
注意事项
- 不要在导航图中为两个顶层目的地互相创建action跳转,这类action默认带压栈逻辑,仅适合非顶层的页面跳转场景
- 如果测试阶段已经出现过Fragment堆叠的问题,清除应用任务栈重进,或者重装应用即可恢复正常
内容的提问来源于stack exchange,提问作者Izaac Doyle
相关产品推荐
相关产品推荐

