自定义BottomNavigationView行为:切换标签时弹出搜索Fragment
问题
我用BottomNavigationView做了三个标签页,第二个标签(B页,对应navigation_dashboard)是起始页。在B页执行搜索后,会打开Fragment D(navigation_laws_filtered),此时B页的导航栈变成 B -> D。按返回键能回到B页,但如果切换到A(navigation_home)或C(navigation_notifications)标签后再切回B,D页还会留在栈里显示。
Navigation组件的XML里支持popUpInclusive属性,但BottomNavigationView默认的导航不需要在XML里定义动作,我找不到重写这个行为的方法。试过监听OnItemViewSelectedListener但不知道后续怎么操作;用fragmentManager.replace()不够优雅;navigateUp()感觉合适但没正确用上,还怕不符合Navigation规范。想知道怎么实现「切换到其他标签时自动把搜索Fragment D从栈里弹出」的效果?
对应的导航XML代码:
<?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/mobile_navigation" app:startDestination="@+id/navigation_dashboard"> <fragment android:id="@+id/navigation_home" android:name="ru.home.government.screens.deputies.DeputiesFragment" android:label="@string/title_deputies" tools:layout="@layout/fragment_deputies"> <argument android:name="EXTRA_NO_BOTTOM_VIEW" app:argType="boolean" android:defaultValue="false" /> <action android:id="@+id/action_navigation_home_to_navigation_dashboard" app:destination="@id/navigation_dashboard" app:popUpTo="@+id/navigation_home" app:popUpToInclusive="true" /> </fragment> <fragment android:id="@+id/navigation_dashboard" android:name="ru.home.government.screens.laws.main.LawsMainFragment" android:label="@string/title_laws" tools:layout="@layout/fragment_laws" > <action android:id="@+id/action_navigation_dashboard_to_lawsFilteredFragment" app:destination="@id/navigation_laws_filtered" /> <action android:id="@+id/action_navigation_dashboard_to_navigation_home" app:destination="@id/navigation_home" app:popUpTo="@+id/navigation_dashboard" app:popUpToInclusive="true" /> <action android:id="@+id/action_navigation_dashboard_to_navigation_notifications" app:destination="@id/navigation_notifications" app:popUpTo="@+id/navigation_dashboard" app:popUpToInclusive="true" /> </fragment> <fragment android:id="@+id/navigation_notifications" android:name="ru.home.government.screens.tracker.TrackerFragment" android:label="@string/title_tracker" tools:layout="@layout/fragment_tracker"> <action android:id="@+id/action_navigation_notifications_to_navigation_dashboard" app:destination="@id/navigation_dashboard" app:popUpTo="@+id/navigation_home" app:popUpToInclusive="true" /> </fragment> <fragment android:id="@+id/navigation_laws_filtered" android:name="ru.home.government.screens.laws.filter.LawsFilteredFragment" android:label="@string/title_search" tools:layout="@layout/fragment_law_filtered"> <argument android:name="EXTRA_KEY" app:argType="string" android:defaultValue="false" /> <action android:id="@+id/action_navigation_laws_filtered_to_navigation_home" app:destination="@id/navigation_home" app:popUpTo="@+id/navigation_dashboard" app:popUpToInclusive="true" /> <action android:id="@+id/action_navigation_laws_filtered_to_navigation_notifications" app:destination="@id/navigation_notifications" app:popUpTo="@+id/navigation_dashboard" app:popUpToInclusive="true" /> </fragment> </navigation>
解决方案
直接通过监听BottomNavigationView的选中事件,结合Navigation组件的popBackStack()方法实现,完全符合Navigation规范,步骤如下:
- 获取NavController和BottomNavigationView实例:
val navController = findNavController(R.id.nav_host_fragment) val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
- 设置
OnItemSelectedListener,在切换到非B标签时,检查当前栈顶是否是D页,若是则弹出栈回到B页:
bottomNav.setOnItemSelectedListener { item -> // 当选中的不是B标签时 if (item.itemId != R.id.navigation_dashboard) { // 检查当前页面是否是搜索后的D页 if (navController.currentDestination?.id == R.id.navigation_laws_filtered) { // 弹出栈到B页,false表示保留B页本身 navController.popBackStack(R.id.navigation_dashboard, false) } } // 执行BottomNavigation默认的导航逻辑 NavigationUI.onNavDestinationSelected(item, navController) }
- 额外优化:调整D页到A/C页的action配置,确保从D页直接切换标签时也能清除栈(将
popUpToInclusive改为false,保留B页作为栈底,方便后续切回):
<action android:id="@+id/action_navigation_laws_filtered_to_navigation_home" app:destination="@id/navigation_home" app:popUpTo="@+id/navigation_dashboard" app:popUpToInclusive="false" /> <action android:id="@+id/action_navigation_laws_filtered_to_navigation_notifications" app:destination="@id/navigation_notifications" app:popUpTo="@+id/navigation_dashboard" app:popUpToInclusive="false" />
这样处理后,只要用户离开B标签,不管是从B页还是D页切换,都会自动把D页从栈中移除,下次切回B标签时只会显示初始的B页,完全符合需求。
内容的提问来源于stack exchange,提问作者Gleichmut
相关产品推荐
相关产品推荐

