具有多个堆栈的Android JetPack导航

时间:2018-08-06 12:40:29

标签: android android-architecture-components android-jetpack android-architecture-navigation

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

我正在寻找一种解决方案,以使每个选项卡具有不同的堆栈,因此当我返回到每个选项卡时,每个选项卡的状态都将保留,而且我也不喜欢将所有这些片段都保留在内存中,因为它的性能很差。对性能的影响,在jetpack导航之前,我使用了这个库https://github.com/ncapdevi/FragNav,这确实可以实现,现在我在寻找与jetpack导航相同的东西。

任何帮助或建议,将不胜感激。

3 个答案:

答案 0 :(得分:5)

编辑2:尽管仍然没有一流的支持(撰写本文时),但Google现在更新了示例,并提供了他们认为应如何解决的示例:{{3} }


主要原因是您仅使用一个NavHostFragment来容纳应用程序的整个后盖。

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

  • 在主布局中,用FrameLayout包裹每个标签片段。
  • 每个选项卡片段都是NavHostFragment,并包含其自己的导航图,以使每个选项卡片段都具有自己的后向堆栈。
  • BottomNavigationView.OnNavigationItemSelectedListener添加BottomNavigtionView来处理每个FrameLayout的可见性。

这还可以解决您的“ ...我不希望将所有这些片段都保留在内存中...”,因为默认情况下使用NavHostFragment进行导航时使用fragmentTransaction.replace(),即始终只能包含NavHostFragment个片段。其余的只是在导航图的后堆栈中。

修改:Google正在开发本机实现https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample


更多详细信息

假设您有一个BottomNavigationView,其中有两个菜单选项,DogsCats

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

以及对应的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的对应名称。在您的Cat框架布局上,设置android:visibility="invisible"

现在,您可以在MainActivity的{​​{1}}中

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()的{​​{1}}的可见性。因此,请确保以某种方式保存它们,例如在FrameLayout

NavHostFragment

现在可以轻松切换哪个onCreateView应该是可见的还是不可见的。

答案 1 :(得分:2)

Android 团队已在最新版本 2.4.0-alpha01 中解决了该问题,现在无需任何解决方法即可支持多个后台堆栈和底部导航。

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

答案 2 :(得分: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();