编辑2: 虽然目前仍然没有一流的支持,但是 Google 现在已经更新了他们的示例,展示了他们认为现在应该如何解决这个问题: https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample
主要原因是您只使用了一个 NavHostFragment
来保存整个应用程序的返回栈。
解决方案是每个选项卡应该持有自己的返回栈。
- 在主布局中,使用一个
FrameLayout
包装每个选项卡片段。
- 每个选项卡片段都是一个
NavHostFragment
并包含自己的导航图,以使每个选项卡片段具有自己的返回栈。
- 向
BottomNavigationView
添加一个 BottomNavigationView.OnNavigationItemSelectedListener
来处理每个 FrameLayout
的可见性。
这也解决了您关于“我不喜欢将所有这些片段保留在内存中”的问题,因为默认情况下,使用 NavHostFragment
进行导航时,它使用 fragmentTransaction.replace()
,即您将始终只有与 NavHostFragment
相同数量的片段。其余部分就在你的导航图的返回栈中。
编辑: Google 正在开发本地实现https://issuetracker.google.com/issues/80029773#comment25
更详细的内容
假设您有一个带有 2 个菜单选项的 BottomNavigationView
,即 Dogs
和 Cats
。
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/dogMenu"
.../>
<item android:id="@+id/catMenu"
.../>
</menu>
接下来你需要两个导航图,例如 dog_navigation_graph.xml
和 cat_navigation_graph.xml
。
dog_navigation_graph
可能长这样:
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dog_navigation_graph"
app:startDestination="@id/dogMenu">
</navigation>
对于cat_navigation_graph
也做相应的处理。
在你的activity_main.xml
中,添加2个NavHostFragment
。
<FrameLayout
android:id="@+id/frame_dog"
...>
<fragment
android:id="@+id/dog_navigation_host_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/dog_navigation_graph"
app:defaultNavHost="true"/>
</FrameLayout>
在下面添加相应的猫的NavHostFragment
。在你的猫的帧布局上,设置android:visibility="invisible"
现在,在你的MainActivity
的onCreateView
中,你可以:
bottom_navigation_view.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.dogMenu -> showHostView(host = 0)
R.id.catMenu -> showHostView(host = 1)
}
return@setOnNavigationItemSelectedListener true
}
showHostView()
所做的只是切换包装NavHostFragment
的FrameLayout
的可见性。因此,请确保以某种方式保存它们,例如在onCreateView
中。
val hostViews = arrayListOf<FrameLayout>()
hostViews.apply {
add(findViewById(R.id.frame_dog))
add(findViewById(R.id.frame_cat))
}
现在很容易切换哪些hostViews
应该是可见和不可见的。