处理多个NavHost时出现问题。此问题与询问的here非常相似。我认为该问题的解决方案也将对我有所帮助,但这是2017年的帖子,至今仍无答案。 Android开发人员文档无济于事,通过网络搜索绝对没有任何可能的帮助。

所以基本上我有一个 Activity 和两个片段。叫他们
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。

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

这里的问题是:在“纵向”模式下,我只使用findNavController().navigate(...),就可以随意浏览片段。但是当我在横向模式下使用平板电脑时,我使用的是“主详细信息流”在同一屏幕上显示“列表”和“详细信息”。有一个如何工作的示例here,我希望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并使其仅替换id为“sideFragmentContainer”的<fragment>,而不是整个屏幕(而不是替换Activity的xml中id为<fragment>fragmentContainer)。

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

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

最佳答案

正如jsmyth886所建议的,this blog post为我指明了正确的方向。
诀窍是使用findFragmentById()直接从片段容器中获取NavHost(在这种情况下,该代码与“主详细信息”屏幕的其余部分共享该屏幕)。这使我可以访问正确的NavController并按预期进行导航。
同样,创建第二个NavGraph也很重要。

因此,请按以下步骤快速操作:

  • 创建主要的NavGraph以进行所有常规导航(如果没有“主细节流”,它将如何工作);
  • 创建一个辅助NavGraph,它仅包含“主详细信息”片段将访问的可能的Destinations。那时没有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。
  • 在代码部分,您应该具有一个 bool(boolean) 标志,以验证您的应用程序是否在Tablet上运行(答案开头提供的链接说明了如何执行此操作)。就我而言,我将其作为MutableLiveData内的ViewModel,就像我可以观察它并相应地更改布局一样。
  • 如果不是平板电脑(即遵循正常的导航流程),只需调用findNavController().navigate(R.id.your_action_id_from_detail_to_some_other_fragment)即可。主要的导航将使用NavController进行;
  • 如果平板电脑使用的是Master Detail Flow,则必须通过找到包含它的NavHost来找到正确的NavController<fragment>,例如:
  • val navHostFragment = childFragmentManager.findFragmentById(R.id. sideFragmentContainer) as NavHostFragment
    
  • 最后,您可以通过调用navHostFragment.navController.navigate(R.id.id_of_the_destination)导航到要显示的Fragment,以将屏幕与Master Detail屏幕的其余部分分开。注意,这里我们不叫Actions,我们直接叫Destination

  • 就是这样,比我想的要简单。感谢LaraMartín的博客文章和jsmyth886为我指出正确的方向!

    10-05 19:51