フラグメントと Kotlin DSL

Navigation コンポーネントは、Kotlin ベースのドメイン固有の言語、つまり DSL は、Kotlin のタイプセーフな ビルダー をタップします。この API を使用すると、XML リソース内ではなく、Kotlin コード内で宣言的にグラフを作成できるため、これは、アプリケーションの開発に 動的ナビゲーションですたとえば、アプリで外部ウェブサービスからナビゲーション設定をダウンロードしてキャッシュし、その設定を使用してアクティビティの onCreate() 関数内でナビゲーション グラフを動的に作成できます。

依存関係

Fragment で Kotlin DSL を使用するには、アプリの build.gradle ファイル:

Groovy

dependencies {
    def nav_version = "2.9.2"

    api "androidx.navigation:navigation-fragment-ktx:$nav_version"
}

Kotlin

dependencies {
    val nav_version = "2.9.2"

    api("androidx.navigation:navigation-fragment-ktx:$nav_version")
}

グラフを作成する

このスライドは、Sunflower アプリ。今回 たとえば、homeplant_detail の 2 つのデスティネーションがあります。home デスティネーションは、ユーザーがアプリを初めて起動したときに表示され、ユーザーの庭にある植物のリストを示します。ユーザーが植物の 1 つを選択すると、アプリは plant_detail デスティネーションに移動します。

図 1 に、これらのディスティネーションと、アプリが home から plant_detail に移動する際に使用するアクション to_plant_detail と、plant_detail デスティネーションで必要となる引数を示します。

Sunflower アプリの 2 つのデスティネーションと、それらを接続するアクション。
図 1: Sunflower アプリの homeplant_detail の 2 つのデスティネーションと、それらを接続するアクション。

Kotlin DSL ナビゲーション グラフをホストする

アプリのナビゲーション グラフを作成する前に、グラフをホストする場所が必要です。この例ではフラグメントを使用して、グラフを 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>

この例では app:navGraph 属性が設定されていないことに注意してください。グラフは res/navigation フォルダでリソースとして定義されていないため、アクティビティの onCreate() プロセスの一部として設定する必要があります。

XML では、アクションはデスティネーション ID を 1 つ以上の引数と関連付けます。ただし、Navigation DSL を使用する場合はルートに引数を含められます。つまり、DSL を使用する場合はアクションという概念はありません。

次のステップでは、グラフを定義するときに使用するルートを定義します。

グラフのルートを作成する

XML ベースのナビゲーション グラフは、Android ビルドプロセスの一環として解析されます。数値定数は id ごとに作成されます。 属性を定義します。ビルド時に生成される静的 ID は、 使用できるため、Navigation DSL データセットが serialization を使用 タイプを あります。各ルートは一意のタイプで表されます。

引数を処理するときに、これらはルートタイプに組み込まれます。これにより、ナビゲーション引数の型安全性を確保できます。

@Serializable data object Home
@Serializable data class Plant(val id: String)

ルートを定義したら、ナビゲーション グラフを作成できます。

val navController = findNavController(R.id.nav_host_fragment)
navController.graph = navController.createGraph(
    startDestination = Home
) {
    fragment<HomeFragment, Home> {
        label = resources.getString(R.string.home_title)
    }
    fragment<PlantDetailFragment, PlantDetail> {
        label = resources.getString(R.string.plant_detail_title)
    }
}

この例では、fragment() DSL ビルダー関数を使用して 2 つのフラグメントのデスティネーションを定義しています。この関数には 2 つの型引数が必要です。

まず、このデスティネーションの UI を提供する Fragment クラス。これを設定すると、XML を使用して定義されたフラグメント デスティネーションで android:name 属性を設定するのと同じ効果があります。

2 つ目は経路ですこれは、Any を拡張するシリアル化可能な型である必要があります。このデスティネーションで使用されるナビゲーション引数とそのタイプを含める必要があります。

この関数は、次のような追加構成用のオプションのラムダも受け入れます。 カスタム引数用の埋め込みビルダー関数も用意されています。 ディープリンクが含まれます

最後に、次のコマンドを使用して home から plant_detail に移動します。 NavController.navigate() 呼び出し:

private fun navigateToPlant(plantId: String) {
   findNavController().navigate(route = PlantDetail(id = plantId))
}

PlantDetailFragment でナビゲーション引数を取得するには、現在の NavBackStackEntry を取得し、その上で toRoute を呼び出してルート インスタンスを取得します。

val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id

PlantDetailFragmentViewModel を使用している場合は、次のコマンドを使用してルート インスタンスを取得します。 SavedStateHandle.toRoute

val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id

このガイドの残りの部分では、一般的なナビゲーション グラフの要素、デスティネーション、グラフの作成時にそれらを使用する方法について説明します。

デスティネーション

Kotlin DSL には、FragmentActivityNavGraph という 3 つのデスティネーション タイプのサポートが組み込まれています。各デスティネーションには、デスティネーションの作成と構成に使用できる独自のインライン拡張関数があります。

Fragment デスティネーション

fragment() DSL 関数は、UI のフラグメント クラスと、このデスティネーションを一意に識別するために使用されるルートタイプでパラメータ化できます。その後にラムダを指定すると、Kotlin DSL グラフで移動するに記載のとおり、追加の設定ができます。

fragment<MyFragment, MyRoute> {
   label = getString(R.string.fragment_title)
   // custom argument types, deepLinks
}

Activity デスティネーション

activity() DSL 関数はルートの型パラメータを受け取りますが、 実装します。代わりに、オプションの activityClass を 後置ラムダを使用しますこの柔軟性により、サービス アカウントに対するアクティビティのデスティネーションを定義し、 暗黙的な依存関係を使用して起動する必要があるアクティビティを、 意味がありません。Fragment デスティネーションと同様に、ラベル、カスタム引数、ディープリンクを設定することもできます。

activity<MyRoute> {
   label = getString(R.string.activity_title)
   // custom argument types, deepLinks...

   activityClass = MyActivity::class
}

navigation() DSL 関数を使用して、ネストされたナビゲーション グラフをビルドできます。この関数は、このグラフに割り当てるルートのタイプ パラメータを受け取ります。また、この関数は次の 2 つの引数を取ります。 グラフの開始デスティネーションのルート、さらに グラフを設定します。有効な要素には、他のデスティネーション、カスタム引数などがあります ディープリンク、商品の説明ラベル destination を使用します。 このラベルは、Terraform を使用して、ナビゲーション グラフを UI コンポーネントにバインドする場合に便利です。 NavigationUI

@Serializable data object HomeGraph
@Serializable data object Home

navigation<HomeGraph>(startDestination = Home) {
   // label, other destinations, deep links
}

カスタム デスティネーションのサポート

Kotlin DSL を直接サポートしていない新しいデスティネーション タイプを使用している場合、これらのデスティネーションを Kotlin DSL に追加するには、addDestination() を使用します。

// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
    route = Graph.CustomDestination.route
}
addDestination(customDestination)

別の方法としては、単項プラス演算子を使用して、新しく作成されたデスティネーションを直接グラフに追加することもできます。

// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
    route = Graph.CustomDestination.route
}

デスティネーション引数を提供する

デスティネーション引数は、ルートクラスの一部として定義できます。これらは、Kotlin クラスと同じ方法で定義できます。必須の引数は null 非許容型として定義され、省略可能な引数はデフォルト値で定義されます。

ルートとその引数を表す基盤となるメカニズムは文字列ベースです。文字列を使用してルートをモデル化すると、構成の変更システム開始プロセスの終了時に、ナビゲーション状態をディスクに保存して復元できます。このため、各ナビゲーション引数はシリアル化可能である必要があります。つまり、引数値のメモリ内表現を String に変換するメソッドが必要です。

Kotlin のシリアル化は プラグイン 基本的なシリアル化のメソッドを自動生成 場合、 @Serializable アノテーションがオブジェクトに追加されます。

@Serializable
data class MyRoute(
  val id: String,
  val myList: List<Int>,
  val optionalArg: String? = null
)

fragment<MyFragment, MyRoute>

カスタム型を指定する

カスタム引数型の場合は、カスタム NavType クラスを指定する必要があります。この を使用すると、ルートまたはディープリンクからタイプの解析方法を正確に制御できます。

たとえば、検索画面を定義するために使用されるルートに、 検索パラメータを表します。

@Serializable
data class SearchRoute(val parameters: SearchParameters)

@Serializable
@Parcelize
data class SearchParameters(
  val searchQuery: String,
  val filters: List<String>
)

カスタム NavType は次のように記述できます。

val SearchParametersType = object : NavType<SearchParameters>(
  isNullableAllowed = false
) {
  override fun put(bundle: Bundle, key: String, value: SearchParameters) {
    bundle.putParcelable(key, value)
  }
  override fun get(bundle: Bundle, key: String): SearchParameters {
    return bundle.getParcelable(key) as SearchParameters
  }

  override fun serializeAsValue(value: SearchParameters): String {
    // Serialized values must always be Uri encoded
    return Uri.encode(Json.encodeToString(value))
  }

  override fun parseValue(value: String): SearchParameters {
    // Navigation takes care of decoding the string
    // before passing it to parseValue()
    return Json.decodeFromString<SearchParameters>(value)
  }
}

その後、その他の型と同様に Kotlin DSL で使用できます。

fragment<SearchFragment, SearchRoute>(
    typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
) {
    label = getString(R.string.plant_search_title)
}

目的地に移動する際は、ルートのインスタンスを作成します。

val params = SearchParameters("rose", listOf("available"))
navController.navigate(route = SearchRoute(params))

このパラメータは、デスティネーションのルートから取得できます。

val searchRoute = navController().getBackStackEntry<SearchRoute>().toRoute<SearchRoute>()
val params = searchRoute.parameters

ディープリンク

ディープリンクは XML によるナビゲーション グラフと同様に、どのデスティネーションにも追加できます。Kotlin DSL を使用してディープリンクを作成するプロセスには、デスティネーションのディープリンクを作成するで定義されている手順が適用されます。

ただし、暗黙的ディープリンクを作成する場合は、分析して <deepLink> 要素に含める XML ナビゲーション リソースが存在しないため、AndroidManifest.xml ファイルに <nav-graph> 要素を追加する方法は利用できず、代わりに手動でアクティビティにインテント フィルタを追加する必要があります。指定するインテント フィルタは、アプリのディープリンクのベースパス、アクション、MIME タイプと一致する必要があります。

ディープリンクは、デスティネーションのラムダ内で deepLink 関数を呼び出すことでデスティネーションに追加されます。パラメータ化された型としてルートを受け取り、ディープリンクに使用される URL のベースパスのパラメータ basePath を受け取ります。

また、 deepLinkBuilder 後置ラムダ。

次の例では、デスティネーション Home へのディープリンク URI が作成されます。

@Serializable data object Home

fragment<HomeFragment, Home>{
  deepLink<Home>(basePath = "www.example.com/home"){
    // Optionally, specify the action and/or mime type that this destination
    // supports
    action = "android.intent.action.MY_ACTION"
    mimeType = "image/*"
  }
}

URI の形式

ディープリンク URI の形式は、次のルールを使用してルートのフィールドから自動的に生成されます。

  • 必須パラメータはパス パラメータとして追加されます(例: /{id})。
  • デフォルト値を持つパラメータ(オプションのパラメータ)は、クエリ parameters(例: ?name={name}
  • コレクションはクエリ パラメータとして追加されます(例: ?items={value1}&items={value2})
  • パラメータの順序は、ルート内のフィールドの順序と一致する

たとえば、次のルートタイプがあるとします。

@Serializable data class PlantDetail(
  val id: String,
  val name: String,
  val colors: List<String>,
  val latinName: String? = null,
)

生成された URI の形式は次のとおりです。

basePath/{id}/{name}/?colors={color1}&colors={color2}&latinName={latinName}

追加できるディープリンクの数に制限はありません。deepLink() を呼び出すたびに、そのデスティネーションで保持されているリストに新しいディープリンクが追加されます。

制限事項

Safe Args プラグインは、Directions クラスと Arguments クラスを生成する XML リソース ファイルを参照するため、Kotlin DSL とは互換性がありません。