簡體   English   中英

如何處理多個 NavHosts/NavControllers?

[英]How to handle multiple NavHosts/NavControllers?

我在處理多個 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 顯示的列表中選擇的 Fruit 的詳細信息。

到目前為止,我們有 Activity -> ListFragment -> DetailFragment。

現在我需要再添加一個 Fragment,稱為 GalleryFragment。 這是一個簡單的片段,顯示所選水果的許多圖片,並在單擊按鈕時由 FruitDetailFragment 調用。

這里的問題是:在縱向模式下,我只需使用findNavController().navigate(...)並按照我想要的方式瀏覽 Fragment。 但是當我在橫向模式下使用平板電腦時,我正在使用主詳細信息流在同一屏幕上顯示列表和詳細信息。 還有就是它是如何工作的例子在這里,我想GalleryFragment更換FruitDetailFragment,水果列表共享屏幕,但到目前為止,我只能設法使其代替“主導航”流動,占據整個screen 並將 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 fragmentContainer<fragment> )。

我沒有找到如何處理多個NavHosts或任何解釋<fragment><fragment>

那么基於此,是否可以使用導航架構並在另一個 Fragment 中顯示一個 Fragment? 為此我需要多個 NavHost,還是有其他方法?

正如jsmyth886所建議的, 這篇博文為我指明了正確的方向。 訣竅是findFragmentById()直接從片段容器中獲取NavHost (在這種情況下,與主細節屏幕的其余部分共享屏幕的那個)。 這使我能夠訪問正確的NavController並按預期進行導航。 創建第二個NavGraph也很重要。

所以一個快速的一步一步:

  • 創建主NavGraph以進行所有常規導航(沒有 Master Detail Flow 時如何工作);
  • 創建一個NavGraph包含 Master Detail 片段將訪問的可能Destinations的輔助NavGraph 沒有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。
  • 在代碼部分,您應該有一個布爾標志來驗證您的應用程序是否在平板電腦上運行(答案開頭提供的鏈接解釋了如何操作)。 就我而言,我將它作為一個MutableLiveData放在我的ViewModel ,這樣我就可以觀察它並相應地更改布局。
  • 如果不是平板電腦(即遵循正常導航流程),只需調用findNavController().navigate(R.id.your_action_id_from_detail_to_some_other_fragment) 主導航將使用主NavController
  • 如果是使用 Master Detail Flow 的平板電腦,則必須通過查找包含它的<fragment>來找到正確的NavHostNavController ,如下所示:
val navHostFragment = childFragmentManager.findFragmentById(R.id. sideFragmentContainer) as NavHostFragment
  • 最后,您可以通過調用navHostFragment.navController.navigate(R.id.id_of_the_destination)導航到要顯示的 Fragment,將屏幕與主詳細信息屏幕的其余部分navHostFragment.navController.navigate(R.id.id_of_the_destination) 請注意,這里我們沒有調用Actions ,而是直接調用Destination

就是這樣,比我想象的要簡單。 感謝 Lara Martín 的博文和 jsmyth886 為我指出正確的方向!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM