3

我在处理多个 NavHost 时遇到问题。这个问题与这里提出的问题非常相似。我认为这个问题的解决方案也会对我有所帮助,但这是 2017 年的帖子,仍然没有答案。Android 开发者文档没有帮助,通过网络搜索绝对没有任何帮助。

所以基本上我有一个活动和两个片段。我们称它们为 FruitsActivity、FruitListFragment、FruitDetailFragment,其中 FruitsActivity 没有相关代码,其 xml 布局由一个<fragment>标签组成,用作NavHost,如下所示:

<fragment
            android:id="@+id/fragmentContainer"
            android:name="androidx.navigation.fragment.NavHostFragment"
            app:navGraph="@navigation/fruits_nav_graph"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

FruitListFragment 是我的 NavGraph 的 startDestination,它处理来自服务器的水果列表。FruitDetailFragment 显示 FruitListFragment 显示的列表中所选水果的详细信息。

到目前为止,我们有 Activity -> ListFragment -> DetailFragment。

现在我需要再添加一个 Fragment,称为 GalleryFragment。它是一个简单的片段,显示许多所选水果的图片,并在单击按钮时由 FruitDetailFragment 调用。

这里的问题是:在纵向模式下,我只是使用findNavController().navigate(...)并按照我想要的方式浏览片段。但是当我在横向模式下使用平板电脑时,我正在使用该主详细信息流在同一屏幕上显示列表和详细信息。这里有一个如何工作的示例,我希望 GalleryFragment 替换 FruitDetailFragment,与水果列表共享屏幕,但到目前为止我只能设法使其替换“主导航”流,占据整个屏幕并将 FruitListFragment 发送到 Back Stack。

我已经尝试过使用findNavController()方法,但无论我从哪里调用它,我只能一直得到相同NavController的结果,而且它总是以相同的线性方式导航。我试图实现我自己的 NavHost,但我得到并错误“找不到 androidx.navigation.NavHost 的类文件”。

这是我的 FruitsActivity 的 xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <fragment
            android:id="@+id/fragmentContainer"
            android:name="androidx.navigation.fragment.NavHostFragment"
            app:navGraph="@navigation/listing_nav_graph"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </FrameLayout>

</layout>

这是横向模式下 FruitListActivity 的 xml(纵向模式下只有 RecyclerView):

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rvFruits"
                android:layout_weight="3"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="8dp"/>

            <FrameLayout
                android:layout_weight="1"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
                <fragment
                    android:id="@+id/sideFragmentContainer"
                    android:name="fruits.example.FruitDetailFragment"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
            </FrameLayout>

        </LinearLayout>

    </RelativeLayout>

</layout>

现在我想调用 GalleryFragment 并让它只替换<fragment>id 'sideFragmentContainer' 而不是整个屏幕(而不是替换Activity 的 xml 中<fragment>的 id fragmentContainer)。

我没有找到任何关于如何处理多个 NavHosts 或<fragment>inside的解释<fragment>

那么基于此,是否可以使用导航架构并在另一个片段中显示一个片段?我需要多个 NavHost,还是有其他方法?

4

1 回答 1

8

正如jsmyth886所建议的,这篇博文为我指明了正确的方向。诀窍是直接从片段容器中findFragmentById()获取NavHost(在这种情况下,与主细节屏幕的其余部分共享屏幕)。NavController这使我能够按预期访问正确和导航。创造第二个NavGraph也很重要。

所以快速一步一步:

  • 创建 mainNavGraph以进行所有通常的导航(如果没有 Master Detail Flow,它将如何工作);
  • 创建一个NavGraph仅包含DestinationsMaster Detail 片段将访问的可能性的辅助节点。然后没有Actions连接,只是Destinations.
  • 在主<fragment>容器中,设置如下属性:

    <fragment
                android:id="@+id/fragmentContainer"
                android:name="androidx.navigation.fragment.NavHostFragment"
                app:navGraph="@navigation/main_nav_graph"
                app:defaultNavHost="true"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

  • app:defaultNavHost="true"很重要。然后主从布局将如下所示:

    <?xml version="1.0" encoding="utf-8"?>
        <layout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                    <androidx.recyclerview.widget.RecyclerView
                        android:id="@+id/rvFruits"
                        android:layout_weight="3"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:layout_margin="8dp"/>

                    <FrameLayout
                        android:layout_weight="1"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent">
                        <fragment
                            android:id="@+id/sideFragmentContainer"
                            android:name="androidx.navigation.fragment.NavHostFragment"
                            app:navGraph="@navigation/secondary_nav_graph"
                            app:defaultNavHost="false"
                            android:layout_width="match_parent"
                            android:layout_height="match_parent"/>
                    </FrameLayout>

                </LinearLayout>

            </RelativeLayout>

        </layout>

  • 同样,该属性app:defaultNavGraph很重要,在此处将其设置为 false。
  • 在代码部分,您应该有一个布尔标志来验证您的应用程序是否在平板电脑上运行(答案开头提供的链接说明了如何执行此操作)。就我而言,我将它作为MutableLiveData我的内部ViewModel,这样我可以观察它并相应地更改布局。
  • 如果不是平板电脑(即遵循正常的导航流程),只需调用findNavController().navigate(R.id.your_action_id_from_detail_to_some_other_fragment). 主导航将使用 main 进行NavController
  • 如果是使用 Master Detail Flow 的平板电脑,您必须找到正确的NavHostNavController找到<fragment>包含它的平板电脑,如下所示:
val navHostFragment = childFragmentManager.findFragmentById(R.id. sideFragmentContainer) as NavHostFragment
  • 最后,您可以通过调用导航到要显示的片段,将屏幕与主详细信息屏幕的其余部分分开navHostFragment.navController.navigate(R.id.id_of_the_destination)。请注意,这里我们不调用Actions,而是Destination直接调用 。

就是这样,比我想象的要简单。感谢 Lara Martín 的博文和 jsmyth886 为我指明了正确的方向!

于 2019-06-28T13:38:04.830 回答