BottomNavigationView结合Navigation Graph返回按钮问题修复咨询
使用 BottomNavigationView 配合 Navigation Graph 实现底部导航,期望按下返回键直接退出应用,但该行为仅在首页 Fragment 生效;处于其他底部 Fragment 时,按下返回键会先回到首页 Fragment,需再次按返回键才能退出应用。
相关代码
MainActivity 布局
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_root" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".activity.MainActivity"> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottomNavigationView" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" app:labelVisibilityMode="auto" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:menu="@menu/menu_main_bottom" /> <androidx.fragment.app.FragmentContainerView android:id="@+id/host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/nav_main_bottom" /> </androidx.constraintlayout.widget.ConstraintLayout>
nav_main_bottom 导航图
<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_nav_bottom" app:startDestination="@id/homeFragment"> <fragment android:id="@+id/homeFragment" android:name="com.yuan.order.home.HomeFragment" android:label="fragment_home" tools:layout="@layout/fragment_home" /> <fragment android:id="@+id/manageFragment" android:name="com.yuan.order.manage.ManageFragment" android:label="fragment_message" tools:layout="@layout/fragment_manage" /> <fragment android:id="@+id/infoFragment" android:name="com.yuan.order.info.InfoFragment" android:label="fragment_info" tools:layout="@layout/fragment_info" /> </navigation>
MainActivity 代码(ViewBinding)
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) val navHostFragment = supportFragmentManager.findFragmentById(R.id.host_fragment) as NavHostFragment binding.bottomNavigationView.setupWithNavController(navHostFragment.navController) } }
修复方案
方案一:直接重写返回键逻辑(最简单)
重写 MainActivity 的 onBackPressed 方法,忽略默认返回栈行为直接退出应用,也可添加双击退出的交互逻辑:
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private var lastBackPressTime = 0L override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) val navHostFragment = supportFragmentManager.findFragmentById(R.id.host_fragment) as NavHostFragment binding.bottomNavigationView.setupWithNavController(navHostFragment.navController) } override fun onBackPressed() { // 直接退出应用 finish() // 可选:添加双击退出逻辑 // val currentTime = System.currentTimeMillis() // if (currentTime - lastBackPressTime > 2000) { // Toast.makeText(this, "再按一次退出应用", Toast.LENGTH_SHORT).show() // lastBackPressTime = currentTime // } else { // finish() // } } }
方案二:修改导航行为,清空返回栈
替换默认的 setupWithNavController,手动处理底部导航点击事件,每次切换页面时清空返回栈,让当前页面成为返回栈唯一页面:
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) val navHostFragment = supportFragmentManager.findFragmentById(R.id.host_fragment) as NavHostFragment val navController = navHostFragment.navController binding.bottomNavigationView.setOnItemSelectedListener { menuItem -> when (menuItem.itemId) { R.id.homeFragment -> { navController.navigate(R.id.homeFragment) { // 清空返回栈到首页并包含首页,确保返回栈只有当前页面 popUpTo(R.id.homeFragment) { inclusive = true } } true } R.id.manageFragment -> { navController.navigate(R.id.manageFragment) { popUpTo(R.id.homeFragment) { inclusive = true } } true } R.id.infoFragment -> { navController.navigate(R.id.infoFragment) { popUpTo(R.id.homeFragment) { inclusive = true } } true } else -> false } } } }
原理说明
默认的 setupWithNavController 会将每次切换的底部 Fragment 添加到返回栈,导致从其他 Fragment 返回时回到首页。上述两种方案,要么直接跳过返回栈逻辑退出应用,要么清空返回栈让当前页面成为栈底,从而实现按返回键直接退出的需求。
内容的提问来源于stack exchange,提问作者Martian
相关产品推荐
相关产品推荐

