デスティネーションに移動する

デスティネーションへの移動は、NavHost 内でアプリ ナビゲーションを管理するオブジェクトである NavController を使用して実行されます。各 NavHost は、それぞれ独自の NavController を持ちます。NavController は、デスティネーションに移動するための方法を複数提供します。この点については、以下のセクションで説明します。

フラグメントや、アクティビティ、ビューの NavController を取得するには、以下のいずれかの方法を使用します。

Kotlin:

Java:

NavController を取得した後、navigate() のいずれかのオーバーロードを呼び出すことで、デスティネーション間を移動できます。各オーバーロードは、さまざまなナビゲーション シナリオをサポートします。詳細については、以下のセクションをご覧ください。

Safe Args を使用してタイプセーフに移動する

デスティネーション間を移動する際は、Safe Args Gradle プラグインを使用することをおすすめします。このプラグインは、デスティネーション間でタイプセーフなナビゲーションを可能にするシンプルなオブジェクトとビルダークラスを生成します。Safe Args は、デスティネーション間の移動とデータの受け渡しの両方で推奨されます。

Safe Args をプロジェクトに追加するには、最上位の build.gradle ファイルに次の classpath を含めます。

Groovy

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.5.3"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

Kotlin

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.5.3"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}

また、使用可能な 2 つのプラグインのいずれかを適用する必要があります。

Java モジュールまたは Java と Kotlin の混合モジュールに適した Java 言語コードを生成するには、アプリまたはモジュールbuild.gradle ファイルに次の行を追加します。

Groovy

plugins {
  id 'androidx.navigation.safeargs'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs")
}

あるいは、Kotlin のみのモジュールに適した Kotlin コードを生成するには、次の行を追加します。

Groovy

plugins {
  id 'androidx.navigation.safeargs.kotlin'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs.kotlin")
}

AndroidX への移行にあるとおり、gradle.properties ファイルandroid.useAndroidX=true が必要です。

Safe Args を有効にすると、定義した各アクション用のクラスとメソッド、ならびに送信側と受信側の各デスティネーションに対応するクラスを格納したコードが生成されます。

Safe Args は、アクションの発生元となる送信側デスティネーションごとにクラスを生成します。生成されるクラス名は、送信側デスティネーションのクラス名に「Directions」という語を追加したものになります。たとえば、送信側デスティネーションが「SpecifyAmountFragment」という名前の場合、「SpecifyAmountFragmentDirections」という名前のクラスが生成されます。

生成されるクラスには、送信側デスティネーション内で定義されている各アクション用の静的メソッドが格納されます。このメソッドは、定義済みのアクション パラメータを引数として受け取り、NavDirections オブジェクトを返します。このオブジェクトは直接 navigate() に渡すことができます。

Safe Args の例

たとえば、SpecifyAmountFragmentConfirmationFragment という 2 つのデスティネーションを接続する単一のアクションを持つナビゲーション グラフがあるとします。ConfirmationFragment は、単一の float パラメータを取ります。このパラメータは、アクションの一部として指定します。

Safe Args は、単一のメソッド「actionSpecifyAmountFragmentToConfirmationFragment()」を持つ SpecifyAmountFragmentDirections クラスと、「ActionSpecifyAmountFragmentToConfirmationFragment」という名前の内部クラスを生成します。この内部クラスは NavDirections から導出され、関連するアクション ID と float パラメータを格納します。返された NavDirections オブジェクトは、navigate() に直接渡すことができます。以下の例をご覧ください。

Kotlin

override fun onClick(v: View) {
    val amount: Float = ...
    val action =
        SpecifyAmountFragmentDirections
            .actionSpecifyAmountFragmentToConfirmationFragment(amount)
    v.findNavController().navigate(action)
}

Java

@Override
public void onClick(View view) {
    float amount = ...;
    action =
        SpecifyAmountFragmentDirections
            .actionSpecifyAmountFragmentToConfirmationFragment(amount);
    Navigation.findNavController(view).navigate(action);
}

Safe Args を使用してデスティネーション間でデータを渡す方法については、Safe Args を使用してタイプセーフにデータを渡すをご覧ください。

ID を使用して移動する

navigate(int) は、アクションまたはデスティネーションのリソース ID を受け取ります。ViewTransactionsFragment に移動する方法を以下のコード スニペットに示します。

Kotlin

viewTransactionsButton.setOnClickListener { view ->
   view.findNavController().navigate(R.id.viewTransactionsAction)
}

Java

viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
    }
});

ボタンの場合、Navigation クラスの createNavigateOnClickListener() コンビニエンス メソッドを使用してデスティネーションに移動することもできます。以下の例をご覧ください。

Kotlin

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null))

Java

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));

トップ アプリバーやボトム ナビゲーションなど、他の一般的な UI コンポーネントの処理方法については、NavigationUI を使用して UI コンポーネントを更新するをご覧ください。

ナビゲーション グラフ内でアクションを定義すると、Navigation コンポーネントは、それに対応する NavAction クラスを生成します。このクラスには、対象のアクション用に定義された以下のような構成が格納されます。

  • デスティネーション: ターゲット デスティネーションのリソース ID。
  • デフォルト引数: ターゲット デスティネーションのデフォルト値を格納する android.os.Bundle(指定されている場合)。
  • ナビゲーション オプション: NavOptions として表現されるナビゲーション オプション。このクラスには、アニメーション リソース構成や、ポップ動作、デスティネーションをシングルトップ モードで起動するかどうかの設定など、ターゲット デスティネーションとの間の遷移に関する特別な構成がすべて格納されます。

2 つの画面で構成され、一方の画面から他方の画面へ移動するアクションを持つグラフの例について考えてみましょう。

<?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/a">

    <fragment android:id="@+id/a"
              android:name="com.example.myapplication.FragmentA"
              android:label="a"
              tools:layout="@layout/a">
        <action android:id="@+id/action_a_to_b"
                app:destination="@id/b"
                app:enterAnim="@anim/nav_default_enter_anim"
                app:exitAnim="@anim/nav_default_exit_anim"
                app:popEnterAnim="@anim/nav_default_pop_enter_anim"
                app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
    </fragment>

    <fragment android:id="@+id/b"
              android:name="com.example.myapplication.FragmentB"
              android:label="b"
              tools:layout="@layout/b">
        <action android:id="@+id/action_b_to_a"
                app:destination="@id/a"
                app:enterAnim="@anim/nav_default_enter_anim"
                app:exitAnim="@anim/nav_default_exit_anim"
                app:popEnterAnim="@anim/nav_default_pop_enter_anim"
                app:popExitAnim="@anim/nav_default_pop_exit_anim"
                app:popUpTo="@+id/a"
                app:popUpToInclusive="true"/>
    </fragment>
</navigation>

ナビゲーション グラフがインフレートされるときに、アクションが解析され、対応する NavAction オブジェクトが、グラフ内で定義されている構成に基づいて生成されます。たとえば、action_b_to_a は、デスティネーション b からデスティネーション a へのナビゲーションとして定義されます。アクションには、各種のアニメーションと、バックスタックからすべてのデスティネーションを削除する popTo 動作が含まれています。この設定はすべて NavOptions としてキャプチャされ、NavAction にアタッチされます。

この NavAction に従うには、NavController.navigate() を使用して、アクションの ID を渡します。以下の例をご覧ください。

Kotlin

findNavController().navigate(R.id.action_b_to_a)

Java

NavigationHostFragment.findNavController(this).navigate(R.id.action_b_to_a);

DeepLinkRequest を使用して移動する

navigate(NavDeepLinkRequest) を使用して、暗黙的ディープリンク デスティネーションに直接移動できます。以下の例をご覧ください。

Kotlin

val request = NavDeepLinkRequest.Builder
    .fromUri("android-app://androidx.navigation.app/profile".toUri())
    .build()
findNavController().navigate(request)

Java

NavDeepLinkRequest request = NavDeepLinkRequest.Builder
    .fromUri(Uri.parse("android-app://androidx.navigation.app/profile"))
    .build()
NavHostFragment.findNavController(this).navigate(request)

NavDeepLinkRequest に加えて、Uri でもアクションと MIME タイプのディープリンクがサポートされています。リクエストにアクションを追加するには、fromAction() または setAction() を使用します。MIME タイプをリクエストに追加するには、fromMimeType() または setMimeType() を使用します。

NavDeepLinkRequest を暗黙的ディープリンク デスティネーションに適切に一致させるには、URI、アクション、MIME タイプをすべてデスティネーションの NavDeepLink に一致させる必要があります。URI はパターンが一致しており、アクションは完全に一致している必要があります。また、MIME タイプは相互に関連している必要があります(たとえば「image/jpg」は「image/*」と一致します)。

アクション ID やデスティネーション ID を使用したナビゲーションとは異なり、デスティネーションが表示されているかどうかにかかわりなく、グラフ内の任意のディープリンクに移動できます。移動先は、現在のグラフ上にあるデスティネーションでも、まったく別のグラフ上にあるデスティネーションでも構いません。

NavDeepLinkRequest を使用して移動する場合、バックスタックはリセットされません。この動作は、他のディープリンク ナビゲーションとは異なります。他のディープリンク ナビゲーションの場合は、ナビゲーション時にバックスタックが置き換えられます。popUpTopopUpToInclusive に関しては、ID を使用して移動した場合と同様に、バックスタックからデスティネーションが削除されます。

ナビゲーションとバックスタック

Android は、ユーザーがアクセスしたデスティネーションを格納するバックスタックを保持します。ユーザーがアプリを開くと、アプリの開始デスティネーションがスタックの最上部に配置されます。navigate() メソッドが呼び出されるたびに、別のデスティネーションがスタックの最上部に配置されます。[上へ] や [戻る] をタップすると、それぞれ NavController.navigateUp() メソッドと NavController.popBackStack() メソッドが呼び出され、スタックの最上部にあるデスティネーションが削除(ポップ)されます。

NavController.popBackStack() は、正常にポップされて別のデスティネーションに戻ったかどうかを示すブール値を返します。false が返される最も一般的なケースは、グラフの開始デスティネーションを手動でポップした場合です。

このメソッドが false を返すと、NavController.getCurrentDestination()null を返します。新しいデスティネーションに移動するか、アクティビティに対して finish() を呼び出してポップを処理する必要があります。以下の例をご覧ください。

Kotlin

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish()
}

Java

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish();
}

popUpTo と popUpToInclusive

アクションを使用して移動する場合、必要に応じて、追加のデスティネーションをバックスタックからポップできます。たとえば、アプリに初期ログインフローがある場合、一度ユーザーがログインした後は、[戻る] ボタンをタップしてもログインフローに戻ることがないように、ログイン関連デスティネーションをすべてバックスタックからポップする必要があります。

デスティネーション間を移動する際にデスティネーションをポップするには、対象の<action> 要素に app:popUpTo 属性を追加します。app:popUpTo は、Navigation ライブラリに対し、navigate() 呼び出しの一環として、バックスタックからいくつかのデスティネーションをポップするように指示します。属性値として、スタック上に残す最も新しいデスティネーションの ID を指定します。

app:popUpToInclusive="true" を追加すると、app:popUpTo 内で指定したデスティネーションもバックスタックから削除するように指示できます。

popUpTo の例: 循環型ロジック

アプリに A、B、C という 3 つのデスティネーションがあり、A から B、B から C、C から A に移動するアクションを持っているとします。対応するナビゲーション グラフを図 1 に示します。

図 1:3 つのデスティネーション(A、B、C)がある循環型ナビゲーション グラフ

ナビゲーション アクションごとに、デスティネーションがバックスタックに追加されます。このナビゲーション フローを繰り返した場合、バックスタックに各デスティネーションの複数のセット(A、B、C、A、B、C、A、以下繰り返し)が格納されるようになります。この繰り返しを回避するには、デスティネーション C からデスティネーション A に移動するアクション内で app:popUpToapp:popUpToInclusive を指定します。以下の例をご覧ください。

<fragment
    android:id="@+id/c"
    android:name="com.example.myapplication.C"
    android:label="fragment_c"
    tools:layout="@layout/fragment_c">

    <action
        android:id="@+id/action_c_to_a"
        app:destination="@id/a"
        app:popUpTo="@+id/a"
        app:popUpToInclusive="true"/>
</fragment>

デスティネーション C に到達した後、バックスタックには各デスティネーション(A、B、C)のインスタンスが 1 つずつ含まれています。デスティネーション A に戻るとき、A を基準に popUpTo を実行します。つまり、このナビゲーション中に B と C をスタックから削除します。また、app:popUpToInclusive="true" を使用することで、最初の A もスタックからポップされ、実質的にスタックがクリアされます。app:popUpToInclusive を使用しなかった場合、バックスタックにはデスティネーション A のインスタンスが 2 つ格納されることになります。