Navigation コンポーネントが提供する Kotlin ベースのドメイン固有言語(DSL)は、Kotlin のタイプセーフ ビルダーを利用しています。この API を使用すると、XML リソース内ではなく、Kotlin コード内で宣言的にグラフを作成できるため、アプリのナビゲーションを動的に作成したい場合に便利です。たとえば、アプリで外部ウェブサービスからナビゲーション設定をダウンロードしてキャッシュし、その設定を使用してアクティビティの onCreate()
関数内でナビゲーション グラフを動的に作成できます。
依存関係
Kotlin DSL を使用するには、アプリの build.gradle
ファイルに次の依存関係を追加します。
Groovy
dependencies { def nav_version = "2.5.3" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.5.3" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
グラフを作成する
まずは、Sunflower アプリに基づく基本的な例を見てみましょう。この例では、home
と plant_detail
の 2 つのデスティネーションがあります。home
デスティネーションは、ユーザーがアプリを初めて起動したときに表示され、ユーザーの庭にある植物のリストを示します。ユーザーが植物の 1 つを選択すると、アプリは plant_detail
デスティネーションに移動します。
図 1 に、これらのディスティネーションと、アプリが home
から plant_detail
に移動する際に使用するアクション to_plant_detail
と、plant_detail
デスティネーションで必要となる引数を示します。

home
と plant_detail
の 2 つのデスティネーションと、それらを接続するアクション。Kotlin DSL ナビゲーション グラフのホストを作成する
グラフの作成方法にかかわらず、NavHost
でグラフをホストする必要があります。Sunflower はフラグメントを使用するため、次の例のように FragmentContainerView
内で NavHostFragment
を使用します。
<!-- activity_garden.xml -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true" />
</FrameLayout>
この例では、グラフは XML リソースとして定義されるのではなくプログラムによって作成されるため、app:navGraph
属性が設定されていないことに注意してください。
グラフの定数を作成する
XML ベースのナビゲーション グラフを使用する場合、Android のビルドプロセスはグラフリソース ファイルを解析し、グラフで定義された id
属性ごとに数値定数を定義します。これらの定数には、生成されたリソースクラス R.id
を介してコードからアクセスできます。
たとえば、次の XML グラフ スニペットは、id
と home
を使用してフラグメント デスティネーションを宣言します。
<navigation ...>
<fragment android:id="@+id/home" ... />
...
</navigation>
ビルドプロセスは、このデスティネーションに関連付けられた定数値 R.id.home
を作成します。コードからこのデスティネーションを参照するには、この定数値を使用します。
Kotlin DSL を使用してプログラムでグラフを作成する場合、このような解析と定数の生成は行われません。代わりに、id
値を持つデスティネーション、アクション、引数ごとに独自の定数を定義する必要があります。各 ID は一意でなければならず、設定が変わっても一貫性を維持する必要があります。
定数を系統立てて作成する方法としては、次の例に示すように、Kotlin object
のセットをネストにして作成し、定数を静的に定義する方法があります。
object nav_graph {
const val id = 1 // graph id
object dest {
const val home = 2
const val plant_detail = 3
}
object action {
const val to_plant_detail = 4
}
object args {
const val plant_id = "plantId"
}
}
この構造の場合、コード内で ID 値にアクセスするには、次の例に示すようにオブジェクトの呼び出しをチェーン接続します。
nav_graph.id // graph id
nav_graph.dest.home // home destination id
nav_graph.action.to_plant_detail // action home -> plant_detail id
nav_graph.args.plant_id // destination argument name
NavGraphBuilder DSL でグラフを作成する
ID の最初のセットを定義したら、ナビゲーション グラフを作成できます。NavController.createGraph()
拡張関数を使用して NavGraph
を作成し、グラフの id
、startDestination
の ID 値、グラフの構造を定義する後置ラムダを渡します。
アクティビティの onCreate()
関数でグラフを作成できます。createGraph()
は Navgraph
を返します。これは、NavHost
に関連付けられた NavController
の graph
プロパティに代入できます。
class GardenActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_garden)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host) as NavHostFragment
navHostFragment.navController.apply {
graph = createGraph(nav_graph.id, nav_graph.dest.home) {
fragment<HomeViewPagerFragment>(nav_graph.dest.home) {
label = getString(R.string.home_title)
action(nav_graph.action.to_plant_detail) {
destinationId = nav_graph.dest.plant_detail
}
}
fragment<PlantDetailFragment>(nav_graph.dest.plant_detail) {
label = getString(R.string.plant_detail_title)
argument(nav_graph.args.plant_id) {
type = NavType.StringType
}
}
}
}
}
}
この例では、fragment()
DSL ビルダー関数を使用し、後置のラムダ構文で 2 つのフラグメントのデスティネーションを定義しています。この関数にはデスティネーションの ID を必須とし、デスティネーションの label
など、設定を追加する場合はオプションのラムダを受け入れます。また、アクション、引数、ディープリンク用の埋め込みビルダー関数も受け入れます。
各デスティネーションの UI を管理する Fragment
クラスは、パラメータ化された型として山かっこ(<>
)で囲まれて渡されます。これは、XML を使用して定義されたフラグメント デスティネーションで android:name
属性を設定するのと同じ効果があります。
Kotlin DSL グラフで移動する
グラフを作成して設定すると、次の例のように NavController.navigate()
を使用して home
から plant_detail
に移動できます。
private fun navigateToPlant(plantId: String) {
val args = bundleOf(nav_graph.args.plant_id to plantId)
findNavController().navigate(nav_graph.action.to_plant_detail, args)
}
サポートされるデスティネーション タイプ
Kotlin DSL は Fragment
、Activity
、NavGraph
デスティネーションをサポートしており、デスティネーションの作成と設定のためのインライン拡張関数がそれぞれに存在します。
Fragment デスティネーション
fragment()
DSL 関数は、その実装である Fragment
クラスにパラメータ化できます。この関数は、このデスティネーションに割り当てる一意の ID を取ります。追加の設定を提供する場合はラムダも取ります。
fragment<FragmentDestination>(nav_graph.dest.fragment_dest_id) {
label = getString(R.string.fragment_title)
// arguments, actions, deepLinks...
}
Activity デスティネーション
activity()
DSL 関数は、このデスティネーションに割り当てる一意の ID を受け取りますが、実装したアクティビティ クラスにパラメータ化されません。代わりに、後置のラムダ構文でオプションの activityClass
を設定できます。この柔軟さのお陰で、明示的なアクティビティ クラスでは意味を成さない場合に、暗黙的インテントを指定して起動するアクティビティの Activitiy デスティネーションを定義できます。なお、Fragment デスティネーションと同様に、ラベルと任意の引数を定義して設定することもできます。
activity(nav_graph.dest.activity_dest_id) {
label = getString(R.string.activity_title)
// arguments, actions, deepLinks...
activityClass = ActivityDestination::class
}
Navigation graph デスティネーション
navigation()
DSL 関数を使用して、ネストされたナビゲーション グラフを作成できます。他のデスティネーション タイプと同様に、この DSL 関数は、グラフに割り当てる ID、グラフの開始デスティネーション ID、グラフの設定を追加するためのラムダ、という 3 つの引数を受け取ります。ラムダの有効な要素には、引数、アクション、他のデスティネーション、ディープリンク、ラベルがあります。
navigation(nav_graph.dest.nav_graph_dest, nav_graph.dest.start_dest) {
// label, arguments, actions, other destinations, deep links
}
カスタム デスティネーションのサポート
デフォルトで直接サポートされていないカスタム デスティネーションを Kotlin DSL に追加するには、次の例に示すように addDestination()
を使用します。
// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
id = nav_graph.dest.custom_dest_id
}
addDestination(customDestination)
また、次のように、単項プラス演算子(+
)を使用して、新しく作成されたデスティネーションをグラフに直接追加することもできます。
// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
id = nav_graph.dest.custom_dest_id
}
デスティネーション引数を提供する
いずれのデスティネーション タイプにも、オプションまたは必須の引数を定義できます。引数を定義するには、すべてのデスティネーションのビルダータイプの基本クラスである NavDestinationBuilder
で argument()
関数を呼び出します。この関数は引数の名前を String
として取り、NavArgument
の作成と設定に使用できるラムダも取ります。ラムダ内では、引数のデータ型、デフォルト値(該当する場合)、引数の値を null
にできるかどうかを指定できます。
fragment<PlantDetailFragment>(nav_graph.dest.plant_detail) {
label = getString(R.string.plant_details_title)
argument(nav_graph.args.plant_name) {
type = NavType.StringType
defaultValue = getString(R.string.default_plant_name)
nullable = true // default false
}
}
defaultValue
を指定する場合、type
は省略可能です。この場合、type
を指定しなければデータ型は defaultValue
から推測されます。defaultValue
と type
の両方を指定する場合、データ型を一致させる必要があります。引数のデータ型の一覧については、NavType
をご覧ください。
アクション
上記に説明したいずれのデスティネーション内でもアクションを定義できます。これには、ルート ナビゲーション グラフ内のグローバル アクションも含まれます。アクションを定義するには、NavDestinationBuilder.action()
関数を使用し、関数の ID を指定します。その他の設定にはラムダを指定します。
次の例では、destinationId
、遷移アニメーション、ポップ動作とシングルトップ動作を含むアクションを作成します。
action(nav_graph.action.to_plant_detail) {
destinationId = nav_graph.dest.plant_detail
navOptions {
anim {
enter = R.anim.nav_default_enter_anim
exit = R.anim.nav_default_exit_anim
popEnter = R.anim.nav_default_pop_enter_anim
popExit = R.anim.nav_default_pop_exit_anim
}
popUpTo(nav_graph.dest.start_dest) {
inclusive = true // default false
}
// if popping exclusively, you can specify popUpTo as
// a property. e.g. popUpTo = nav_graph.dest.start_dest
launchSingleTop = true // default false
}
}
ディープリンク
XML ベースのナビゲーション グラフと同様に、いずれのデスティネーションにもディープリンクを追加できます。Kotlin DSL を使用して明示的ディープリンクを作成するプロセスには、デスティネーションのディープリンクを作成するで定義した手順が適用されます。
しかし、暗黙的ディープリンクを作成する場合は、分析して <deepLink>
要素に含める XML ナビゲーション リソースが存在しないため、AndroidManifest.xml
ファイルに <nav-graph>
要素を追加する方法も利用できず、代わりにアクティビティに手動でインテント フィルタを追加する必要があります。指定するインテント フィルタは、アプリのディープリンクのベース URL パターンと一致させる必要があります。
個別にディープリンクされたデスティネーションごとに deepLink()
DSL 関数を使用して、より具体的な URI パターンを指定できます。次の例に示すように、この関数は URI パターンを String
で指定します。
deepLink("http://www.example.com/plants/")
追加できるディープリンク URI の数に制限はありません。deepLink()
を呼び出すたびに、そのデスティネーションに固有の内部リストに新しいディープリンクが追加されます。
以下に、パスベースとクエリベースのパラメータを定義して、より複雑な暗黙的ディープリンクを指定する場合について示します。
val baseUri = "http://www.example.com/plants"
fragment<PlantDetailFragment>(nav_graph.dest.plant_detail) {
label = getString(R.string.plant_details_title)
deepLink("${baseUri}/{id}")
deepLink("${baseUri}/{id}?name={plant_name}")
argument(nav_graph.args.plant_id) {
type = NavType.IntType
}
argument(nav_graph.args.plant_name) {
type = NavType.StringType
nullable = true
}
}
文字列補間を使用すると、定義を簡略化できます。
ID を作成する
Navigation ライブラリでは、グラフ要素に使用される ID 値は、設定が変更されても一貫性を保つ一意の整数である必要があります。これらの ID を作成する方法の 1 つとしては、グラフの定数を作成するで示すような静的定数として定義する方法があります。XML でリソースとして静的リソース ID を定義することもできます。また、ID を動的に作成することもできます。たとえば、参照するたびに増分するシーケンス カウンタを作成できます。
object nav_graph {
// Counter for id's. First ID will be 1.
var id_counter = 1
val id = id_counter++
object dest {
val home = id_counter++
val plant_detail = id_counter++
}
object action {
val to_plant_detail = id_counter++
}
object args {
const val plant_id = "plantId"
}
}
制限事項
- Safe Args プラグインは、
Directions
クラスとArguments
クラスを生成する XML リソース ファイルを参照するため、Kotlin DSL とは互換性がありません。