[英]How to handle multiple NavHosts/NavControllers?
I'm having a problem when dealing with multiple NavHosts.我在处理多个 NavHost 时遇到问题。 This issue is very similar to the one asked here .
这个问题与这里提出的问题非常相似。 I think the solution for this question would help me as well, but it's a post from 2017 and it still has no answer.
我认为这个问题的解决方案对我也有帮助,但它是 2017 年的帖子,仍然没有答案。 Android Developers Documentation doesn't help and searching through the web shows absolutely nothing that could possibly help.
Android 开发人员文档无济于事,通过网络搜索绝对没有任何可能有帮助的内容。
So basically I have one Activity and two Fragments.所以基本上我有一个活动和两个片段。 Let's call them FruitsActivity, FruitListFragment, FruitDetailFragment, where FruitsActivity has no relevant code and its xml layout is composed by a
<fragment>
tag, serving as NavHost
, like that:让我们称它们为 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"/>
The FruitListFragment is the startDestination of my NavGraph, it handles a list of fruits that will come from the server. FruitListFragment 是我的 NavGraph 的 startDestination,它处理来自服务器的水果列表。 The FruitDetailFragment shows details about the Fruit selected in the list displayed by FruitListFragment.
FruitDetailFragment 显示有关在 FruitListFragment 显示的列表中选择的 Fruit 的详细信息。
So far we have Activity -> ListFragment -> DetailFragment.到目前为止,我们有 Activity -> ListFragment -> DetailFragment。
Now I need to add one more Fragment, called GalleryFragment.现在我需要再添加一个 Fragment,称为 GalleryFragment。 It's a simple fragment that displays many pictures of the Fruit selected and it's called by FruitDetailFragment when clicking a button.
这是一个简单的片段,显示所选水果的许多图片,并在单击按钮时由 FruitDetailFragment 调用。
The problem here is: In Portrait mode I simply use findNavController().navigate(...)
and I navigate through the Fragments like I want.这里的问题是:在纵向模式下,我只需使用
findNavController().navigate(...)
并按照我想要的方式浏览 Fragment。 but when I'm using a Tablet in Landscape mode, I'm using that Master Detail Flow to display List and Details on the same screen.但是当我在横向模式下使用平板电脑时,我正在使用主详细信息流在同一屏幕上显示列表和详细信息。 There is an example of how it works here , and I want the GalleryFragment to replace the FruitDetailFragment, sharing the screen with the list of fruits, but so far I could only manage to make it replace the "main navigation" flow, occupying the entire screen and sending the FruitListFragment to the Back Stack.
还有就是它是如何工作的例子在这里,我想GalleryFragment更换FruitDetailFragment,水果列表共享屏幕,但到目前为止,我只能设法使其代替“主导航”流动,占据整个screen 并将 FruitListFragment 发送到 Back Stack。
I already tried to play around with findNavController()
method, but no matter from where I call it I can only get the same NavController
all the time, and it always navigates in the same linear way.我已经尝试使用
findNavController()
方法,但是无论我从哪里调用它,我都只能始终获得相同的NavController
,并且它始终以相同的线性方式导航。 I tried to implement my own NavHost, but I get and error "class file for androidx.navigation.NavHost not found".我试图实现我自己的 NavHost,但我得到并错误“找不到 androidx.navigation.NavHost 的类文件”。
This is the xml of my FruitsActivity:这是我的 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>
This is the xml of the FruitListActivity in Landscape mode (in portrait mode there is just the RecyclerView):这是横向模式下 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>
And now I want to call the GalleryFragment and make it replace just the <fragment>
of id 'sideFragmentContainer' instead of the whole screen (instead of replacing the <fragment>
of id fragmentContainer
in the Activity's xml).现在我想调用 GalleryFragment 并让它只替换 id 'sideFragmentContainer' 的
<fragment>
而不是整个屏幕(而不是替换 Activity xml 中 id fragmentContainer
的<fragment>
)。
I didn't find any explanations of how to handle multiple NavHosts or <fragment>
inside <fragment>
.我没有找到如何处理多个NavHosts或任何解释
<fragment>
内<fragment>
。
So based on that, is it possible to use Navigation Architecture and display a Fragment inside another Fragment?那么基于此,是否可以使用导航架构并在另一个 Fragment 中显示一个 Fragment? Do I need multiple NavHosts for that, or is there another way?
为此我需要多个 NavHost,还是有其他方法?
As suggested by jsmyth886 , this blog post pointed me to the right direction.正如jsmyth886所建议的, 这篇博文为我指明了正确的方向。 The trick was to
findFragmentById()
to get the NavHost
directly from the fragment container (in this case, the one sharing the screen with the rest of the Master Detail screen).诀窍是
findFragmentById()
直接从片段容器中获取NavHost
(在这种情况下,与主细节屏幕的其余部分共享屏幕的那个)。 This allowed me to access the correct NavController
and navigate as expected.这使我能够访问正确的
NavController
并按预期进行导航。 It's important to create a second NavGraph
too.创建第二个
NavGraph
也很重要。
So a quick step-by-step:所以一个快速的一步一步:
NavGraph
to make all the usual navigation (how it would work without the Master Detail Flow);NavGraph
以进行所有常规导航(没有 Master Detail Flow 时如何工作);NavGraph
containing only the possible Destinations
that the Master Detail fragment will access.NavGraph
包含 Master Detail 片段将访问的可能Destinations
的辅助NavGraph
。 No Actions
connecting then, just the Destinations
.Actions
连接,只有Destinations
。<fragment>
container, set the attributes like that:<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"
is important. app:defaultNavHost="true"
很重要。 Then the Master Detail layout will look like that:
<?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
is important, set it to false here.app:defaultNavGraph
很重要,在这里将其设置为 false。MutableLiveData
inside my ViewModel
, like that I can observe it and change layouts accordingly.MutableLiveData
放在我的ViewModel
,这样我就可以观察它并相应地更改布局。findNavController().navigate(R.id.your_action_id_from_detail_to_some_other_fragment)
.findNavController().navigate(R.id.your_action_id_from_detail_to_some_other_fragment)
。 The main navigation will happen using the main NavController
;NavController
;NavHost
and NavController
by finding the <fragment>
that contains it, like that:<fragment>
来找到正确的NavHost
和NavController
,如下所示:val navHostFragment = childFragmentManager.findFragmentById(R.id. sideFragmentContainer) as NavHostFragment
navHostFragment.navController.navigate(R.id.id_of_the_destination)
.navHostFragment.navController.navigate(R.id.id_of_the_destination)
导航到要显示的 Fragment,将屏幕与主详细信息屏幕的其余部分navHostFragment.navController.navigate(R.id.id_of_the_destination)
。 Notice that here we don't call Actions
, we call the Destination
directly.Actions
,而是直接调用Destination
。 That's it, simpler than what I thought.就是这样,比我想象的要简单。 Thank you Lara Martín for the blog post and jsmyth886 for pointing me to the right direction!
感谢 Lara Martín 的博文和 jsmyth886 为我指出正确的方向!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.