通常來說,巢狀導覽圖最適合呈現應用程式內的登入流程、精靈或其他子流程。像這樣為獨立的子導覽流程建立巢狀結構,應用程式 UI 的主要流程將更容易理解與管理。
此外,巢狀圖還可以重複使用。這類圖表也提供一定程度的封裝,也就是說,巢狀圖以外的目的地無法直接存取圖表中的任何目的地。這些目的地應 navigate()
至巢狀圖本身,這張圖表的內部邏輯可以變更,不會影響其餘部分。
範例
就應用程式的「頂層」導覽圖而言,應該將起始處設在應用程式啟動時首先向使用者顯示的目的地,而且應包含他們在應用程式中到處瀏覽時看到的目的地。
以圖 1 的頂層導覽圖為例,假設您只想要求使用者在首次啟動應用程式時查看 title_screen 和 register 畫面。之後,系統會儲存使用者資訊,而當應用程式後續啟動時,應該直接將使用者導向「match」畫面。
最佳做法是將「match」畫面設為頂層導覽圖的「起始目的地」,然後將標題和註冊畫面移至巢狀圖,如圖 1 所示:
當「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 編輯器建立巢狀圖,步驟如下:
- 在 Navigation 編輯器中按住 Shift 鍵,然後點選要加入巢狀圖的目的地。
按一下滑鼠右鍵即可開啟內容選單,然後依序選取「Move to Nested Graph」>「New Graph」。這些到達網頁會包含巢狀圖中。圖 2 顯示導覽編輯器中的巢狀圖:
按一下巢狀圖。「Attributes」面板中會顯示以下屬性:
- 類型:包含「巢狀圖」
- ID:包含巢狀圖的系統指派 ID。這個 ID 用於在程式碼中參照的巢狀圖。
按兩下巢狀圖即可顯示其到達網頁。
按一下「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>
在程式碼中,將根圖表連結動作的資源 ID 傳遞至巢狀圖:
Kotlin
view.findNavController().navigate(R.id.action_mainFragment_to_sendMoneyGraph)
Java
Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
- 回到「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">
<strong><include app:graph="@navigation/included_graph" /></strong>
<fragment
android:id="@+id/fragment"
android:name="com.example.myapplication.BlankFragment"
android:label="Fragment in Root Graph"
tools:layout="@layout/fragment_blank">
<strong><action
android:id="@+id/action_fragment_to_second_graph"
app:destination="@id/second_graph" /></strong>
</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"
<strong>android:id="@+id/second_graph"</strong>
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>
其他資源
如要進一步瞭解導覽功能,請參閱下列其他資源。