BottomNavigationView切换标签时如何避免使用导航栈的自定义动画?
问题背景
使用bottomNavigation.setupWithNavController(this.navHostFragment.navController)配置底部导航与Navigation组件结合,底部导航包含两个标签页(tab1、tab2),每个标签对应独立的嵌套导航图,且标签内部通过带自定义滑动动画的action堆叠Fragment。当前问题为:当标签内存在Fragment堆叠时,切换标签会复用栈顶Fragment的action动画,而非标签切换的默认动画;替换R.animator下的nav_default_*默认动画仅对标签根Fragment生效。
已知问题说明
该问题是Google官方的已知Bug(编号194113170),表现为:使用BottomNavigationView结合Navigation组件时,若标签对应的导航栈存在Fragment堆叠,切换标签会错误应用栈顶Fragment的进入/退出动画,而非标签切换的默认动画。
临时修复方案
方案1:通过Destination监听重置动画
给NavController添加OnDestinationChangedListener,在导航到标签根页面时强制切换为默认标签动画,非根页面恢复自定义动画:
navController.addOnDestinationChangedListener { _, destination, _ -> val fragmentNavigator = navController.navigatorProvider.getNavigator(FragmentNavigator::class.java) val isTabRoot = listOf(R.id.tab1, R.id.tab2).contains(destination.id) if (isTabRoot) { // 应用标签切换的默认动画 fragmentNavigator.apply { enterAnim = R.anim.nav_default_enter_anim exitAnim = R.anim.nav_default_exit_anim popEnterAnim = R.anim.nav_default_pop_enter_anim popExitAnim = R.anim.nav_default_pop_exit_anim } } else { // 恢复页面跳转的自定义滑动动画 fragmentNavigator.apply { enterAnim = R.anim.slide_in_right exitAnim = R.anim.slide_out_left popEnterAnim = R.anim.slide_in_left popExitAnim = R.anim.slide_out_right } } }
方案2:重写BottomNavigation导航逻辑
自定义扩展函数,在切换标签时临时替换动画,完成后恢复:
fun BottomNavigationView.setupWithNavControllerFix(navController: NavController) { this.setupWithNavController(navController) this.setOnItemSelectedListener { item -> val tabRootIds = listOf(R.id.tab1, R.id.tab2) if (item.itemId in tabRootIds) { val fragmentNavigator = navController.navigatorProvider.getNavigator(FragmentNavigator::class.java) // 保存当前动画配置 val originalAnims = listOf( fragmentNavigator.enterAnim, fragmentNavigator.exitAnim, fragmentNavigator.popEnterAnim, fragmentNavigator.popExitAnim ) // 临时设置标签切换默认动画 fragmentNavigator.apply { enterAnim = R.anim.nav_default_enter_anim exitAnim = R.anim.nav_default_exit_anim popEnterAnim = R.anim.nav_default_pop_enter_anim popExitAnim = R.anim.nav_default_pop_exit_anim } // 执行标签导航 navController.navigate(item.itemId) // 恢复原有动画配置 fragmentNavigator.apply { enterAnim = originalAnims[0] exitAnim = originalAnims[1] popEnterAnim = originalAnims[2] popExitAnim = originalAnims[3] } true } else { NavigationUI.onNavDestinationSelected(item, navController) } } }
使用时替换原配置代码:
bottomNavigation.setupWithNavControllerFix(this.navHostFragment.navController)
方案3:降级Navigation组件版本
可临时降级到Bug出现前的版本(如Navigation Component 2.3.0),但不推荐长期使用,建议优先等待官方修复。
内容的提问来源于stack exchange,提问作者rbrn
相关产品推荐
相关产品推荐

