特定Fragment中导航抽屉汉堡/返回图标切换失效问题
问题:进入SettingsFragment后无法切换导航图标为返回箭头
我尝试在打开SettingsFragment时将导航图标从汉堡样式切换为返回箭头,参考了两种方法但均无效,进入该Fragment后仍显示汉堡图标。
当前显示效果截图:
相关代码
MainActivity
@AndroidEntryPoint class MainActivity : AppCompatActivity(), HideShowIconInterface { var APP_START_TIME: Long = 0 private lateinit var appBarConfiguration: AppBarConfiguration private var _binding: ActivityMainBinding? = null val binding get() = _binding!! private lateinit var navController: NavController private lateinit var postViewModel: PostViewModel private var _navGraph: NavGraph? = null private val navGraph get() = _navGraph!! lateinit var adView: AdView private var adRequest: AdRequest? = null private val applicationScope = CoroutineScope(Dispatchers.Unconfined) lateinit var drawerLayout: DrawerLayout lateinit var drawerToggle: ActionBarDrawerToggle override fun onDestroy() { super.onDestroy() adView.destroy() adRequest = null _binding = null } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) APP_START_TIME = System.currentTimeMillis() _binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) adView = AdView(this) delayedInit() postViewModel = ViewModelProvider(this)[PostViewModel::class.java] setSupportActionBar(binding.toolbar) drawerLayout = binding.drawerLayout drawerToggle = ActionBarDrawerToggle( this, drawerLayout, binding.toolbar, R.string.app_name, R.string.app_name ).apply { drawerLayout.addDrawerListener(this) this.syncState() } initBackStackChangeListener() val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? if (navHostFragment != null) { navController = navHostFragment.navController } appBarConfiguration = AppBarConfiguration( setOf( R.id.nav_home, R.id.nav_accessory, R.id.nav_arcade, R.id.nav_fashion, R.id.nav_food, R.id.nav_heath, R.id.nav_lifestyle, R.id.nav_sports, R.id.nav_favorites, R.id.settingsFragment ), drawerLayout ) setupActionBarWithNavController(this, navController, appBarConfiguration) setupWithNavController(binding.navView, navController) _navGraph = navController.navInflater.inflate(R.navigation.mobile_navigation) onBackPressedDispatcher.addCallback(this /* lifecycle owner */, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { finish() } }) } override fun onResume() { super.onResume() adView.resume() postViewModel.currentDestination.observe(this) { currentDestination -> Log.w(TAG, "currentDestination: at first run is $currentDestination") navGraph.setStartDestination(currentDestination) navController.graph = navGraph } navController.addOnDestinationChangedListener { _, destination, _ -> Log.d(TAG, "addOnDestinationChangedListener: " + destination.id) if (destination.id != R.id.settingsFragment && destination.id != R.id.aboutFragment && destination.id != R.id.privacyPolicyFragment && destination.id != R.id.termsAndConditionsFragment ) { postViewModel.saveCurrentDestination(destination.id) } } } override fun onSupportNavigateUp(): Boolean { return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() } override fun showHamburgerIcon() { supportActionBar?.setDisplayHomeAsUpEnabled(false) drawerToggle.setDrawerIndicatorEnabled(true) } override fun showBackIcon() { supportActionBar?.setDisplayHomeAsUpEnabled(true) binding.toolbar.setNavigationIcon(R.drawable.ic_arrow_back_24) drawerToggle.setDrawerIndicatorEnabled(false) } private fun initBackStackChangeListener() { supportFragmentManager.addOnBackStackChangedListener { val fragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) if (fragment is SettingsFragment) { binding.toolbar.setNavigationIcon(R.drawable.ic_arrow_back_24) drawerToggle.isDrawerIndicatorEnabled = false drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) drawerToggle.setToolbarNavigationClickListener { onBackPressed() } supportActionBar?.setDisplayHomeAsUpEnabled(true) } else { supportActionBar?.setDisplayHomeAsUpEnabled(false) drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) drawerToggle.isDrawerIndicatorEnabled = true drawerToggle.toolbarNavigationClickListener = null drawerToggle.syncState() } } } } interface HideShowIconInterface { fun showHamburgerIcon() fun showBackIcon() }
SettingsFragment
@AndroidEntryPoint class SettingsFragment : Fragment() { private var _binding: FragmentSettingsBinding? = null private val binding get() = _binding!! private val settingsViewModel: SettingsViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentSettingsBinding.inflate(inflater, container, false) (requireActivity() as MainActivity).showBackIcon() return binding.root } }
解决方案
1. 修正AppBarConfiguration配置
将R.id.settingsFragment从顶层目的地集合中移除,让Navigation组件自动识别它为非顶层页面:
appBarConfiguration = AppBarConfiguration( setOf( R.id.nav_home, R.id.nav_accessory, R.id.nav_arcade, R.id.nav_fashion, R.id.nav_food, R.id.nav_heath, R.id.nav_lifestyle, R.id.nav_sports, R.id.nav_favorites ), drawerLayout )
2. 统一图标切换逻辑
删除initBackStackChangeListener方法,以及SettingsFragment中调用showBackIcon()的代码,改用NavController的目的地监听器统一处理:
navController.addOnDestinationChangedListener { _, destination, _ -> Log.d(TAG, "当前目的地: ${destination.id}") if (destination.id == R.id.settingsFragment || destination.id == R.id.aboutFragment || destination.id == R.id.privacyPolicyFragment || destination.id == R.id.termsAndConditionsFragment) { // 显示返回箭头并锁定抽屉 drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) drawerToggle.isDrawerIndicatorEnabled = false supportActionBar?.setDisplayHomeAsUpEnabled(true) drawerToggle.setToolbarNavigationClickListener { navController.navigateUp() } } else { // 恢复汉堡图标并解锁抽屉 drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) drawerToggle.isDrawerIndicatorEnabled = true supportActionBar?.setDisplayHomeAsUpEnabled(false) drawerToggle.setToolbarNavigationClickListener(null) drawerToggle.syncState() } // 保留原有保存目的地逻辑 if (destination.id != R.id.settingsFragment && destination.id != R.id.aboutFragment && destination.id != R.id.privacyPolicyFragment && destination.id != R.id.termsAndConditionsFragment ) { postViewModel.saveCurrentDestination(destination.id) } }
3. 移除冗余代码
删除HideShowIconInterface接口以及showHamburgerIcon、showBackIcon方法,避免逻辑冲突。
问题根源说明
之前的方法无效,是因为你把SettingsFragment设为了AppBarConfiguration的顶层目的地,Navigation组件会强制显示汉堡图标,手动修改图标会被组件逻辑覆盖。同时多个地方(Fragment、BackStackChangeListener)同时修改图标状态,容易造成状态冲突,统一用NavController的目的地监听器是最可靠的处理方式。
内容的提问来源于stack exchange,提问作者Dr Mido
相关产品推荐
相关产品推荐

