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

特定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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 14:25:30