14

我正在使用version 1.0.0-alpha04带有底部导航的 Jetpack Navigation。它可以工作,但导航不正确。例如,如果我有选项卡 A 和选项卡 B,从选项卡 AI 转到页面 C,然后从那里转到选项卡 B 并再次返回选项卡 A,我将在选项卡 A 中看到根片段,而不是页面 C不是我所期望的。

我正在寻找一种解决方案,为每个选项卡提供不同的堆栈,因此当我回到它时,每个选项卡的状态都会被保留,而且我不喜欢将所有这些片段保留在内存中,因为它有一个坏的对性能的影响,在喷气背包导航之前,我使用了这个库https://github.com/ncapdevi/FragNav,这正是它的作用,现在我正在寻找与喷气背包导航相同的东西。

4

3 回答 3

16

编辑 2:虽然仍然没有一流的支持(截至撰写本文时),但 Google 现在已经更新了他们的示例,并提供了一个他们认为现在应该如何解决的示例:https ://github.com/googlesamples/android-architecture-组件/树/主/NavigationAdvancedSample


主要原因是您只使用一个NavHostFragment来保存应用程序的整个后台堆栈。

解决方案是每个选项卡都应该拥有自己的后台堆栈。

  • 在您的主布局中,用FrameLayout.
  • 每个选项卡片段都是一个NavHostFragment并包含自己的导航图,以使每个选项卡片段都有自己的后退堆栈。
  • 添加一个BottomNavigationView.OnNavigationItemSelectedListenertoBottomNavigtionView来处理每个 FrameLayout 的可见性。

这也照顾了您的“...我不喜欢将所有这些片段保存在内存中...”,因为NavHostFragment默认情况下使用Navigation fragmentTransaction.replace(),即您将始终只有与NavHostFragments 一样多的片段。其余的只是在导航图的后面堆栈中。

编辑:谷歌正在开发本地实现https://issuetracker.google.com/issues/80029773#comment25


更详细

假设您有BottomNavigationView2 个菜单选项,Dogs并且Cats.

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/dogMenu"
        .../>

    <item android:id="@+id/catMenu"
        .../>
</menu>

然后你需要 2 个导航图,比如dog_navigation_graph.xmlcat_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>

和对应的 for cat_navigation_graph

在您的activity_main.xml中,添加 2NavHostFragment

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

并在下面为您的 cat 添加相应的内容NavHostFragment。在你的猫框架布局上,设置android:visibility="invisible"

现在,在你MainActivityonCreateView你可以

bottom_navigation_view.setOnNavigationItemSelectedListener { item ->
    when (item.itemId) {
        R.id.dogMenu -> showHostView(host = 0)
        R.id.catMenu -> showHostView(host = 1)
    }
    return@setOnNavigationItemSelectedListener true
}

所做showHostView()的只是切换FrameLayout包装NavHostFragments 的 s 的可见性。所以一定要以某种方式保存它们,例如onCreateView

val hostViews = arrayListOf<FrameLayout>()  // Member variable of MainActivity
hostViews.apply {
    add(findViewById(R.id.frame_dog))
    add(findViewById(R.id.frame_cat))
}

现在很容易切换hostViews应该可见和不可见的。

于 2018-09-03T09:06:08.163 回答
2

该问题已由 Android 团队在最新版本 2.4.0-alpha01 中解决,现在可以在没有任何解决方法的情况下使用多个 backstacks 以及底部导航支持。

https://developer.android.com/jetpack/androidx/releases/navigation

于 2021-06-01T04:01:00.057 回答
0

首先,我想对@Algar 的回答进行编辑。您要隐藏的框架应该有android:visibility="gone"而不是invisible. 在你的主要布局中你会有这样的原因:

    <LinearLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".ui.activity.MainActivity">
    
        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar_base" />
    
        <FrameLayout
            android:id="@+id/frame_home"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="2"
            >
            <fragment
                android:id="@+id/home_navigation_host_fragment"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:defaultNavHost="true"
                app:navGraph="@navigation/home_nav" />
        </FrameLayout>
        <FrameLayout
            android:id="@+id/frame_find"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="2"
            android:visibility="gone">
    
            <fragment
                android:id="@+id/find_navigation_host_fragment"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:defaultNavHost="true"
                app:navGraph="@navigation/find_nav" />
        </FrameLayout>
        ...
    
   </LinearLayout>

如果您将 main 包装在 LinearLayout 中,将框架设置为不可见仍然会使该框架计数,因此 BottomNavigation 不会出现。

其次,您应该创建一个 NavHostFragment 实例(即:curNavHostFragment)以跟踪在单击 BottomNavigation 中的选项卡时哪个 NavHostFragment 可见。注意:当活动因配置更改而被破坏时,您可能希望恢复此 curNavHostFragment。这是一个例子:

@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    //if this activity is restored from previous state,
    //we will have the ItemId of botnav the has been selected
    //so that we can set up nav controller accordingly
    switch (bottomNav.getSelectedItemId()) {
        case R.id.home_fragment:
            curNavHostFragment = homeNavHostFragment;
            ...
            break;
        case R.id.find_products_fragment:
            curNavHostFragment = findNavHostFragment;
            ...
            break;
    
    }
    curNavController = curNavHostFragment.getNavController();
于 2021-03-31T06:07:48.180 回答