一般的に、アプリ内のログインフロー、ウィザード、その他のサブフローは、ネストされたナビゲーション グラフとして表現するのが最も適切です。このように自己完結型のサブナビゲーション フローをネストすることにより、アプリの UI のメインフローの把握と管理が容易になります。
また、ネストグラフは再利用可能です。さらに、一定レベルのカプセル化も実現できます。ネストグラフの外部にあるデスティネーションは、ネストグラフの内部にあるデスティネーションに直接アクセスすることができません。代わりに、navigate()
を使用して、ネストグラフ自体にアクセスする必要があります。ネストグラフの内部ロジックは、グラフの残りの部分に影響を与えることなく変更できます。
例
アプリのトップレベル ナビゲーション グラフは、ユーザーがアプリを起動したときに最初に表示される開始デスティネーションから始まり、ユーザーがアプリ内を移動する際に表示されるすべてのデスティネーションを含む必要があります。
図 1 のトップレベル ナビゲーション グラフの例で、ユーザーがアプリを初めて起動したときに限り、title_screen 画面と register 画面を表示したいとします。さらに、ユーザー情報を保存して、次回以降のアプリの起動時には、直接 match 画面にユーザーを誘導するとします。
ベスト プラクティスとしては、match 画面をトップレベル ナビゲーション グラフの開始デスティネーションに設定し、title_screen 画面と register 画面をネストグラフに移動するのが適切です。図 1 をご覧ください。
match 画面が起動したら、登録済みユーザーがいるかどうかを確認します。ユーザーが登録されていない場合は、ユーザーを登録画面に誘導します。
条件付きナビゲーション シナリオの詳細については、条件付きナビゲーションをご覧ください。
Compose
Compose を使用してネストされたナビゲーション グラフを作成するには、NavGraphBuilder.navigation()
関数を使用します。navigation()
の使い方は、グラフにデスティネーションを追加する際の NavGraphBuilder.composable()
関数や NavGraphBuilder.dialog()
関数の使い方と同様です。
主な違いは、navigation
は新しいデスティネーションではなくネストされたグラフを作成することです。次に、navigation()
のラムダ内で 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 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>
参考情報
ナビゲーションについて詳しくは、以下の参考情報をご確認ください。