巢狀結構圖

通常來說,巢狀導覽圖最適合呈現應用程式內的登入流程、精靈或其他子流程。像這樣為獨立的子導覽流程建立巢狀結構,應用程式 UI 的主要流程將更容易理解與管理。

此外,巢狀圖還可以重複使用。這類圖表也提供一定程度的封裝,也就是說,巢狀圖以外的目的地無法直接存取圖表中的任何目的地。這些目的地應 navigate() 至巢狀圖本身,這張圖表的內部邏輯可以變更,不會影響其餘部分。

範例

就應用程式的「頂層」導覽圖而言,應該將起始處設在應用程式啟動時首先向使用者顯示的目的地,而且應包含他們在應用程式中到處瀏覽時看到的目的地。

圖 1:頂層導覽圖。

以圖 1 的頂層導覽圖為例,假設您只想要求使用者在首次啟動應用程式時查看 title_screenregister 畫面。系統之後會儲存使用者資訊,而當應用程式後續啟動時,應該直接將使用者導向 match 畫面。

最佳做法是將 match 畫面設為頂層導覽圖的「起始目的地」,然後將標題和註冊畫面移至巢狀圖,如圖 1 所示:

圖 2:包含巢狀圖的頂層導覽圖。

match 畫面啟動後,請查看是否有已註冊的使用者,並將尚未註冊的使用者導向註冊畫面。

如需進一步瞭解條件式導覽情境,請參閱「條件式導覽」。

Compose

如要透過 Compose 建立巢狀導覽圖,請使用 NavGraphBuilder.navigation() 函式。將目的地加入圖表時,會用到 navigation(),就像 NavGraphBuilder.composable()NavGraphBuilder.dialog() 函式一樣。

主要差異在於 navigation 會建立巢狀圖,而非新目的地。接著在 navigation() 的 lambda 中呼叫 composable()dialog(),將目的地加入巢狀圖。

請考慮下列程式碼片段如何透過 Compose 實作圖 2 中的圖表:

// Routes @Serializable object Title @Serializable object Register  // Route for nested graph @Serializable object Game  // Routes inside nested graph @Serializable object Match @Serializable object InGame @Serializable object ResultsWinner @Serializable object GameOver  NavHost(navController, startDestination = Title) {    composable<Title> {        TitleScreen(            onPlayClicked = { navController.navigate(route = Register) },            onLeaderboardsClicked = { /* Navigate to leaderboards */ }        )    }    composable<Register> {        RegisterScreen(            onSignUpComplete = { navController.navigate(route = Game) }        )    }    navigation<Game>(startDestination = Match) {        composable<Match> {            MatchScreen(                onStartGame = { navController.navigate(route = InGame) }            )        }        composable<InGame> {            InGameScreen(                onGameWin = { navController.navigate(route = ResultsWinner) },                onGameLose = { navController.navigate(route = GameOver) }            )        }        composable<ResultsWinner> {            ResultsWinnerScreen(                onNextMatchClicked = {                    navController.navigate(route = Match) {                        popUpTo(route = Match) { inclusive = true }                    }                },                onLeaderboardsClicked = { /* Navigate to leaderboards */ }            )        }        composable<GameOver> {            GameOverScreen(                onTryAgainClicked = {                    navController.navigate(route = Match) {                        popUpTo(route = Match) { inclusive = true }                    }                }            )        }    } } 

如要直接導覽至巢狀目的地,請使用路徑類型,就像處理任何其他目的地一樣。這是因為路徑屬於全域概念,可做為任何畫面的導覽目的地:

navController.navigate(route = Match) 

XML

使用 XML 時,可以透過 Navigation 編輯器建立巢狀圖,步驟如下:

  1. 在 Navigation 編輯器中按住 Shift 鍵,然後點選要加入巢狀圖的目的地。
  2. 按一下滑鼠右鍵即可開啟內容選單,然後依序選取「Move to Nested Graph」>「New Graph」。這些到達網頁會包含巢狀圖中。圖 2 顯示導覽編輯器中的巢狀圖:

    圖 3:Navigation 編輯器中的巢狀圖。
  3. 按一下巢狀圖。「Attributes」面板中會顯示以下屬性:

    • 類型:包含「巢狀圖」
    • ID:包含巢狀圖的系統指派 ID。這個 ID 用於在程式碼中參照的巢狀圖。
  4. 按兩下巢狀圖即可顯示其到達網頁。

  5. 按一下「Text」分頁標籤,切換至 XML 檢視畫面。巢狀導覽圖現已加入圖表。這張導覽圖有專屬的 navigation 元素和 ID,還有一個 startDestination 屬性指向巢狀圖中的第一個目的地:

    <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    xmlns:android="http://schemas.android.com/apk/res/android"    app:startDestination="@id/mainFragment">    <fragment        android:id="@+id/mainFragment"        android:name="com.example.cashdog.cashdog.MainFragment"        android:label="fragment_main"        tools:layout="@layout/fragment_main" >        <action            android:id="@+id/action_mainFragment_to_sendMoneyGraph"            app:destination="@id/sendMoneyGraph" />        <action            android:id="@+id/action_mainFragment_to_viewBalanceFragment"            app:destination="@id/viewBalanceFragment" />    </fragment>    <fragment        android:id="@+id/viewBalanceFragment"        android:name="com.example.cashdog.cashdog.ViewBalanceFragment"        android:label="fragment_view_balance"        tools:layout="@layout/fragment_view_balance" />    <navigation android:id="@+id/sendMoneyGraph" app:startDestination="@id/chooseRecipient">        <fragment            android:id="@+id/chooseRecipient"            android:name="com.example.cashdog.cashdog.ChooseRecipient"            android:label="fragment_choose_recipient"            tools:layout="@layout/fragment_choose_recipient">            <action                android:id="@+id/action_chooseRecipient_to_chooseAmountFragment"                app:destination="@id/chooseAmountFragment" />        </fragment>        <fragment            android:id="@+id/chooseAmountFragment"            android:name="com.example.cashdog.cashdog.ChooseAmountFragment"            android:label="fragment_choose_amount"            tools:layout="@layout/fragment_choose_amount" />    </navigation> </navigation> 
  6. 在程式碼中,將根圖表連結動作的資源 ID 傳遞至巢狀圖:

Kotlin

view.findNavController().navigate(R.id.action_mainFragment_to_sendMoneyGraph) 

Java

Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph); 
  1. 回到「Design」分頁,按一下「Root」即可返回根圖表。

使用 include 參照其他導覽圖

如要將圖表結構模組化,也可以使用父項導覽圖中的 <include> 元素,將某張圖表「加入」另一張圖表。這樣就能在個別模組或整個專案中定義所含圖表,盡量重複利用。

下列程式碼片段將示範如何使用 <include>

<!-- (root) nav_graph.xml --> <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:id="@+id/nav_graph"     app:startDestination="@id/fragment">      <include app:graph="@navigation/included_graph" />      <fragment         android:id="@+id/fragment"         android:name="com.example.myapplication.BlankFragment"         android:label="Fragment in Root Graph"         tools:layout="@layout/fragment_blank">         <action             android:id="@+id/action_fragment_to_second_graph"             app:destination="@id/second_graph" />     </fragment>      ... </navigation> 
<!-- included_graph.xml --> <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:id="@+id/second_graph"     app:startDestination="@id/includedStart">      <fragment         android:id="@+id/includedStart"         android:name="com.example.myapplication.IncludedStart"         android:label="fragment_included_start"         tools:layout="@layout/fragment_included_start" /> </navigation>