如何处理多个NavHosts/NavControllers?
How to handle multiple NavHosts/NavControllers?
我在处理多个 NavHost 时遇到问题。这个问题与 here 提出的问题非常相似。我认为这个问题的解决方案也会对我有所帮助,但它是 2017 年的 post,但仍然没有答案。 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 显示的列表中选择的 Fruit 的详细信息。
到目前为止我们有 Activity -> ListFragment -> DetailFragment。
现在我需要再添加一个 Fragment,称为 GalleryFragment。这是一个简单的片段,显示了所选水果的许多图片,并在单击按钮时由 FruitDetailFragment 调用。
这里的问题是:在纵向模式下,我只使用 findNavController().navigate(...)
并按我想要的方式浏览片段。但是当我在横向模式下使用平板电脑时,我正在使用主细节流程在同一屏幕上显示列表和详细信息。有一个如何工作的示例 here,我希望 GalleryFragment 替换 FruitDetailFragment,与水果列表共享屏幕,但到目前为止我只能设法让它替换 "main navigation"流,占据整个屏幕并将 FruitListFragment 发送到 Back Stack。
我已经尝试过使用 findNavController()
方法,但无论我从哪里调用它,我始终只能获得相同的 NavController
,并且它始终以相同的线性方式导航方法。
我尝试实现自己的 NavHost,但出现错误 "class file for androidx.navigation.NavHost not found".
这是我的水果xmlActivity:
<?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>
而不是整个屏幕(而不是替换 id [=] 的 <fragment>
21=] 在 Activity 的 xml).
我没有在 <fragment>
.
中找到任何关于如何处理多个 NavHost 或 <fragment>
的解释
那么基于此,是否可以使用导航架构并在另一个片段中显示一个片段?为此我需要多个 NavHost,还是有其他方法?
正如 jsmyth886, this blog post 所建议的那样,为我指明了正确的方向。
诀窍是 findFragmentById()
直接从片段容器中获取 NavHost
(在本例中,与 Master Detail 屏幕的其余部分共享屏幕)。这使我能够访问正确的 NavController
并按预期导航。
创建第二个 NavGraph
也很重要。
这么快step-by-step:
- 创建主要
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"
很重要。然后 Master Detail 布局将如下所示:
<?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。
- 在代码部分,你应该有一个布尔标志来验证你的应用程序是否在平板电脑上 运行 (答案开头提供的 link 解释了如何做它)。在我的例子中,我将它作为
MutableLiveData
在我的 ViewModel
中,这样我可以观察它并相应地更改布局。
- 如果不是平板电脑(即遵循正常的导航流程),只需调用
findNavController().navigate(R.id.your_action_id_from_detail_to_some_other_fragment)
。主导航将使用 main NavController
;
- 如果是使用 Master Detail Flow 的平板电脑,您必须通过找到包含它的
<fragment>
来找到正确的 NavHost
和 NavController
,例如:
val navHostFragment = childFragmentManager.findFragmentById(R.id. sideFragmentContainer) as NavHostFragment
- 最后,您可以通过调用
navHostFragment.navController.navigate(R.id.id_of_the_destination)
导航到要显示的 Fragment,将屏幕与 Master Detail 屏幕的其余部分分开。注意这里我们不调用Actions
,我们直接调用Destination
就是这样,比我想的要简单。感谢 Lara Martín 的博客 post 和 jsmyth886 为我指明了正确的方向!
我在处理多个 NavHost 时遇到问题。这个问题与 here 提出的问题非常相似。我认为这个问题的解决方案也会对我有所帮助,但它是 2017 年的 post,但仍然没有答案。 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 显示的列表中选择的 Fruit 的详细信息。
到目前为止我们有 Activity -> ListFragment -> DetailFragment。
现在我需要再添加一个 Fragment,称为 GalleryFragment。这是一个简单的片段,显示了所选水果的许多图片,并在单击按钮时由 FruitDetailFragment 调用。
这里的问题是:在纵向模式下,我只使用 findNavController().navigate(...)
并按我想要的方式浏览片段。但是当我在横向模式下使用平板电脑时,我正在使用主细节流程在同一屏幕上显示列表和详细信息。有一个如何工作的示例 here,我希望 GalleryFragment 替换 FruitDetailFragment,与水果列表共享屏幕,但到目前为止我只能设法让它替换 "main navigation"流,占据整个屏幕并将 FruitListFragment 发送到 Back Stack。
我已经尝试过使用 findNavController()
方法,但无论我从哪里调用它,我始终只能获得相同的 NavController
,并且它始终以相同的线性方式导航方法。
我尝试实现自己的 NavHost,但出现错误 "class file for androidx.navigation.NavHost not found".
这是我的水果xmlActivity:
<?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>
而不是整个屏幕(而不是替换 id [=] 的 <fragment>
21=] 在 Activity 的 xml).
我没有在 <fragment>
.
<fragment>
的解释
那么基于此,是否可以使用导航架构并在另一个片段中显示一个片段?为此我需要多个 NavHost,还是有其他方法?
正如 jsmyth886, this blog post 所建议的那样,为我指明了正确的方向。
诀窍是 findFragmentById()
直接从片段容器中获取 NavHost
(在本例中,与 Master Detail 屏幕的其余部分共享屏幕)。这使我能够访问正确的 NavController
并按预期导航。
创建第二个 NavGraph
也很重要。
这么快step-by-step:
- 创建主要
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"
很重要。然后 Master Detail 布局将如下所示:
<?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。 - 在代码部分,你应该有一个布尔标志来验证你的应用程序是否在平板电脑上 运行 (答案开头提供的 link 解释了如何做它)。在我的例子中,我将它作为
MutableLiveData
在我的ViewModel
中,这样我可以观察它并相应地更改布局。 - 如果不是平板电脑(即遵循正常的导航流程),只需调用
findNavController().navigate(R.id.your_action_id_from_detail_to_some_other_fragment)
。主导航将使用 mainNavController
; - 如果是使用 Master Detail Flow 的平板电脑,您必须通过找到包含它的
<fragment>
来找到正确的NavHost
和NavController
,例如:
val navHostFragment = childFragmentManager.findFragmentById(R.id. sideFragmentContainer) as NavHostFragment
- 最后,您可以通过调用
navHostFragment.navController.navigate(R.id.id_of_the_destination)
导航到要显示的 Fragment,将屏幕与 Master Detail 屏幕的其余部分分开。注意这里我们不调用Actions
,我们直接调用Destination
就是这样,比我想的要简单。感谢 Lara Martín 的博客 post 和 jsmyth886 为我指明了正确的方向!