應用程式內的登入流程、精靈或其他子流程通常是巢狀導覽圖的最佳呈現方式。像這樣為獨立的子導覽流程建立巢狀結構,應用程式 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 中的圖表:
NavHost(navController, startDestination = "title_screen") {
composable("title_screen") {
TitleScreen(
onPlayClicked = { navController.navigate("register") },
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("register") {
RegisterScreen(
onSignUpComplete = { navController.navigate("gameInProgress") }
)
}
navigation(startDestination = "match", route = "gameInProgress") {
composable("match") {
MatchScreen(
onStartGame = { navController.navigate("in_game") }
)
}
composable("in_game") {
InGameScreen(
onGameWin = { navController.navigate("results_winner") },
onGameLose = { navController.navigate("game_over") }
)
}
composable("results_winner") {
ResultsWinnerScreen(
onNextMatchClicked = {
navController.navigate("match") {
popUpTo("match") { inclusive = true }
}
},
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("game_over") {
GameOverScreen(
onTryAgainClicked = {
navController.navigate("match") {
popUpTo("match") { inclusive = true }
}
}
)
}
}
}
如要直接「導覽」至巢狀目的地,請使用 route
,與前往任何其他目的地一樣。這是因為路徑是全域概念,任何畫面都能前往:
navController.navigate("match")
擴充功能函式
您可以在 NavGraphBuilder
上使用擴充功能函式,將目的地新增至圖表。您可以將這些擴充函式與預先建構的 navigation
、composable
和 dialog
擴充方法搭配使用。
例如,您可以使用擴充功能函式新增上一節示範的巢狀圖:
fun NavGraphBuilder.addNestedGraph(navController: NavController) {
navigation(startDestination = "match", route = "gameInProgress") {
composable("match") {
MatchScreen(
onStartGame = { navController.navigate("in_game") }
)
}
composable("in_game") {
InGameScreen(
onGameWin = { navController.navigate("results_winner") },
onGameLose = { navController.navigate("game_over") }
)
}
composable("results_winner") {
ResultsWinnerScreen(
onNextMatchClicked = { navController.navigate("match") },
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("game_over") {
GameOverScreen(
onTryAgainClicked = { navController.navigate("match") }
)
}
}
}
接著,您可以在傳遞至 NavHost
的 lambda 中呼叫這個函式,而不是以內嵌方式呼叫導覽。以下範例會示範這個做法:
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(navController, startDestination = "title_screen") {
composable("title_screen") {
TitleScreen(
onPlayClicked = { navController.navigate("register") },
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("register") {
RegisterScreen(
onSignUpComplete = { navController.navigate("gameInProgress") }
)
}
// Add the nested graph using the extension function
addNestedGraph(navController)
}
}
XML
使用 XML 時,您可以使用導覽編輯器建立巢狀圖。步驟如下:
- 在導覽編輯器中,按住 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>
:
<!-- (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>
其他資源
如要進一步瞭解導航,請參閱下列其他資源。