通常、アプリ内のログインフロー、ウィザード、その他のサブフローは、ネストされたナビゲーション グラフとして表現するのが最も効果的です。自己完結型のサブナビゲーション フローをこのようにネストすると、アプリの UI のメインフローの理解と管理が容易になります。
また、ネストグラフを再利用できます。また、一定のレベルのカプセル化も実現します。ネストグラフの外部にあるデスティネーションは、ネストグラフ内のデスティネーションに直接アクセスできません。代わりに、ネストグラフ自体に対して navigate()
する必要があります。内部ロジックは、グラフの他の部分に影響を与えることなく変更できます。
例
アプリのトップレベル ナビゲーション グラフは、ユーザーがアプリを起動したときに最初に表示される最初のデスティネーションから始まり、ユーザーがアプリ内を移動する際に表示されるデスティネーションを含む必要があります。
図 1 のトップレベル ナビゲーション グラフの例で、ユーザーがアプリを初めて起動したときに限り、title_screen 画面と register 画面を表示したいとします。その後、ユーザー情報が保存されます。その後のアプリの起動時には、直接 match 画面を表示する必要があります。
ベスト プラクティスとして、図 1 に示すように、match 画面をトップレベル ナビゲーション グラフの開始デスティネーションとして設定し、タイトル画面と登録画面をネストグラフに移動します。
match 画面が起動したら、登録済みユーザーがいるかどうかを確認します。ユーザーが登録されていない場合は、登録画面に移動します。
条件付きナビゲーションのシナリオの詳細については、条件付きナビゲーションをご覧ください。
Compose
Compose を使用してネストされたナビゲーション グラフを作成するには、NavGraphBuilder.navigation()
関数を使用します。グラフにデスティネーションを追加するときは、NavGraphBuilder.composable()
関数や NavGraphBuilder.dialog()
関数と同様に navigation()
を使用します。
主な違いは、navigation
では新しいデスティネーションではなく、ネストグラフが作成される点です。次に、navigation
のラムダ内で 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
に渡すラムダでこの関数を呼び出すことができます。例は次のとおりです。
@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 を使用する場合は、Navigation Editor を使用してネストグラフを作成できます。手順は次のとおりです。
- Navigation Editor で、Shift キーを長押ししながら、ネストグラフに含めるデスティネーションをクリックします。
右クリックしてコンテキスト メニューを開き、[Move to Nested Graph] > [New Graph] を選択します。対象デスティネーションがネストされたグラフ内に囲い込まれます。Navigation Editor 内のネストされたグラフを図 2 に示します。
ネストグラフをクリックします。次の属性が [Attributes] パネルに表示されます。
- Type - 「ネストされたグラフ」が格納されます。
- 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 を使用して他のナビゲーション グラフを参照する
グラフ構造をモジュール化するもう 1 つの方法は、親ナビゲーション グラフで <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>
参考情報
ナビゲーションについて詳しくは、以下の参考情報をご確認ください。