How to handle multiple NavHosts/NavControllers?

后端 未结 1 1038
佛祖请我去吃肉
佛祖请我去吃肉 2021-02-06 17:19

I\'m having a problem when dealing with multiple NavHosts. This issue is very similar to the one asked here. I think the solution for this question would help me as well, but it

相关标签:
1条回答
  • 2021-02-06 18:07

    As suggested by jsmyth886, this blog post pointed me to the right direction. 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). This allowed me to access the correct NavController and navigate as expected. It's important to create a second NavGraph too.

    So a quick step-by-step:

    • Create the main NavGraph to make all the usual navigation (how it would work without the Master Detail Flow);
    • Create a secondary NavGraph containing only the possible Destinations that the Master Detail fragment will access. No Actions connecting then, just the Destinations.
    • In the main <fragment> container, set the attributes like that:
    
        <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"/>
    
    
    • The app:defaultNavHost="true" is important. 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>
    
    
    • Again, the attribute app:defaultNavGraph is important, set it to false here.
    • In the code part, you should have a boolean flag to verify if your app is running on a Tablet or not (the link provided in the beginning of the answer explains how to do it). In my case, I have it as a MutableLiveData inside my ViewModel, like that I can observe it and change layouts accordingly.
    • If is not tablet (i.e. follows the normal navigation flow), simply call findNavController().navigate(R.id.your_action_id_from_detail_to_some_other_fragment). The main navigation will happen using the main NavController;
    • If is tablet using the Master Detail Flow, you must find the correct NavHost and NavController by finding the <fragment> that contains it, like that:
    val navHostFragment = childFragmentManager.findFragmentById(R.id. sideFragmentContainer) as NavHostFragment
    
    • And finally you can navigate to the Fragment that you want to appear dividing the screen with the rest of the Master Detail screen by calling navHostFragment.navController.navigate(R.id.id_of_the_destination). Notice that here we don't call Actions, we call the Destination directly.

    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!

    0 讨论(0)
提交回复
热议问题