Jetpack Navigation

Navigation アーキテクチャ コンポーネントは、ナビゲーションの実装を簡素化するとともに、アプリのナビゲーション フローを視覚化するためにも役立ちます。このライブラリには、次のような多くの利点があります。

  • フラグメント トランザクションの自動処理
  • 上へ戻るがデフォルトで正しく処理される
  • アニメーションと遷移のデフォルトの動作
  • 高度なオペレーションとしてのディープリンク
  • ナビゲーション UI パターン(ナビゲーション ドロワーやボトム ナビゲーションなど)をわずかな追加作業で実装できる
  • ナビゲーション中のタイプセーフな情報の受け渡し
  • アプリのナビゲーション フローを視覚化して編集できる Android Studio ツール

演習内容

この Codelab では、下記のサンプルアプリを使用します。

すべてのアクティビティとフラグメントはすでに作成済みです。Navigation コンポーネントを使用してそれらを接続し、その過程で以下を実装します。

  • 視覚的なナビゲーション グラフ
  • デスティネーションとアクションによるナビゲーション
  • 遷移アニメーション
  • メニュー ナビゲーション、ボトム ナビゲーション、メニュー ドロワー ナビゲーション
  • タイプセーフな引数の受け渡し
  • ディープリンク

前提条件

  • Kotlin の基礎知識(この Codelab は Kotlin を使用します)
  • Android Studio 3.2 以上
  • API レベル 14 以上を実行するエミュレータまたはデバイス

コードを入手する

GitHub から、ナビゲーション Codelab のクローンを作成します。

$ git clone https://github.com/googlecodelabs/android-navigation

または、リポジトリを ZIP ファイルとしてダウンロードすることもできます。

ZIP をダウンロード

Android Studio 3.3 以上を入手する

Android Studio 3.3 以上を使用していることを確認します。Android Studio ナビゲーション ツールには、このバージョンが必要です。

Android Studio の最新バージョンをダウンロードする必要がある場合は、こちらからダウンロードできます。

ナビゲーションの概要

Navigation コンポーネントは、協調して機能する 3 つの主要部分で構成されています。それらを次に示します。

  1. ナビゲーション グラフ(新しい XML リソース)- すべてのナビゲーション関連情報を 1 つの場所に集めたリソース。アプリ内のすべての場所(「デスティネーション」と呼びます)と、ユーザーがアプリ内でたどる可能性があるパスが含まれています。
  2. NavHostFragment(レイアウト XML ビュー)- レイアウトに追加する特別なウィジェット。ナビゲーション グラフのさまざまなデスティネーションを表示します。
  3. NavController(Kotlin / Java オブジェクト)- ナビゲーション グラフ内の現在の位置をトラッキングするオブジェクト。ユーザーがナビゲーション グラフ内を移動する際の、NavHostFragment 内のデスティネーション コンテンツの差し替え(スワップ)を管理します。

ナビゲーション中は、NavController オブジェクトを使用して、移動先またはナビゲーション グラフ内でたどるパスを指定します。そうすると、NavController が NavHostFragment 内の適切なデスティネーションを表示します。

以上が基本的なコンセプトです。ナビゲーションがどのように行われるかを実際に見てみましょう。最初は、新しいリソースであるナビゲーション グラフです。

デスティネーション

Navigation コンポーネントでは、デスティネーションというコンセプトを導入しています。デスティネーションはアプリ内で移動先として使用できる場所であり、通常はフラグメントまたはアクティビティです。それらはデフォルトでサポートされていますが、必要であれば独自のデスティネーション タイプを作成することもできます。

ナビゲーション グラフは、ユーザーがアプリでたどる可能性があるすべてのパスを定義する新しいリソースタイプです。特定のデスティネーションから到達できるすべてのデスティネーションを視覚的に表示します。Android Studio の Navigation Editor にグラフが表示されます。サンプルアプリで作成する開始ナビゲーション グラフの一部を次に示します。

Home、Step One、Step Two

Navigation Editor の操作

1. res/navigation/mobile_navigation.xml を開きます。

2. [Design] をクリックして、デザインモードに切り替えます。

次のものが表示されます。

使用可能なデスティネーションがナビゲーション グラフに表示されます。デスティネーション間の矢印を「アクション」と呼びます。アクションについては後で詳しく説明します。

3. デスティネーションをクリックして、その属性を表示します。

4. アクション(矢印)をクリックして、その属性を表示します。

ナビゲーション XML ファイルの構造

Layout Editor でレイアウト XML を変更する場合と同様に、グラフィカルな Navigation Editor で行ったすべての変更は、基になっている XML ファイルに反映されます。

[Text] タブをクリックします。

次のような XML が表示されます。

<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"
    app:startDestination="@+id/home_dest">

    <!-- ...tags for fragments and activities here -->

</navigation>

次の点に注目してください。

  • <navigation> は、各ナビゲーション グラフのルートノードです。
  • <navigation> には 1 つ以上のデスティネーションが含まれ、それぞれが <activity> 要素または <fragment> 要素で表されます。
  • app:startDestination は、ユーザーが初めてアプリを開いたときにデフォルトで起動されるデスティネーションを指定する属性です。

フラグメント デスティネーションを見てみましょう。

<fragment
    android:id="@+id/flow_step_one_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment"
    tools:layout="@layout/flow_step_one_fragment">
    <argument
        .../>

    <action
        android:id="@+id/next_action"
        app:destination="@+id/flow_step_two_dest">
    </action>
</fragment>

次の点に注目してください。

  • android:id は、この XML とコードの別の場所でデスティネーションを参照するために使用できるフラグメントの ID を定義しています。
  • android:name は、デスティネーションに移動したときにインスタンス化するフラグメントの完全修飾クラス名を宣言しています。
  • tools:layout は、グラフィカル エディタに表示するレイアウトを指定しています。

一部の <fragment> タグには、<action><argument>,<deepLink> も含まれています。これらについては後で説明します。

サンプルアプリでは、少数のデスティネーションが最初からグラフに含まれています。このステップで、新しいデスティネーションを追加します。デスティネーションにナビゲートするには、ナビゲーション グラフにデスティネーションを追加する必要があります。

1. res/navigation/mobile_navigation.xml を開いて、[Design] タブをクリックします。

2. 新規デスティネーション アイコンをクリックし、「settings_fragment」を選択します。

新しいデスティネーションが作成され、フラグメントのレイアウトのプレビューがデザインビューにレンダリングされます。

なお、XML ファイルを直接編集してデスティネーションを追加することもできます。

mobile_navigation.xml

<fragment
    android:id="@+id/settings_dest"
    android:name="com.example.android.codelabs.navigation.SettingsFragment"
    android:label="@string/settings"
    tools:layout="@layout/settings_fragment" />

これで便利なナビゲーション グラフが作成されましたが、まだ実際にそれを使ってナビゲーションを行ったわけではありません。

アクティビティとナビゲーション

Navigation コンポーネントは、ナビゲーションの原則で概説されているガイダンスに従います。ナビゲーションの原則では、アプリのエントリ ポイントとしてアクティビティを使用することが推奨されています。アクティビティには、ボトム ナビゲーションなどのグローバル ナビゲーションも含まれます。

これに対して、フラグメントはデスティネーション固有の実際のレイアウトになります。

これらすべてを機能させるには、アクティビティ レイアウトを変更して、NavHostFragment という特別なウィジェットを含める必要があります。NavHostFragment は、ナビゲーション グラフ内をナビゲートする際に、さまざまなフラグメント デスティネーションを差し替えます。

上記の画像と似たナビゲーションをサポートする単純なレイアウトは、次のようになります。このコードの例は res/layout-470dp/navigation_activity.xml にあります。

<LinearLayout
    .../>
    <androidx.appcompat.widget.Toolbar
        .../>
    <fragment
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/mobile_navigation"
        app:defaultNavHost="true"
        />
    <com.google.android.material.bottomnavigation.BottomNavigationView
        .../>
</LinearLayout>

次の点に注目してください。

  • これはアクティビティ用のレイアウトです。ボトム ナビゲーションとツールバーを含むグローバル ナビゲーションがあります。
  • android:name="androidx.navigation.fragment.NavHostFragment"app:defaultNavHost="true" により、システムの [戻る] ボタンが NavHostFragment に接続されます。
  • app:navGraph="@navigation/mobile_navigation" は、NavHostFragment をナビゲーション グラフに関連付けます。このナビゲーション グラフは、ユーザーが移動できるすべてのデスティネーションを、この NavHostFragment で指定しています。

最後に、ユーザーがボタンクリックなどの操作を行ったときに、ナビゲーション コマンドをトリガーする必要があります。NavController という特別なクラスは、NavHostFragment 内のフラグメントのスワップをトリガーします。

// Command to navigate to flow_step_one_dest
findNavController().navigate(R.id.flow_step_one_dest)

ナビゲートするには、デスティネーション ID またはアクション ID のいずれかを渡します。それらはナビゲーション グラフ XML で定義されている ID です。サンプルでは、デスティネーション ID を渡しています。

navigate()popBackStack(), などのメソッドを呼び出すとき、NavController は移動先または移動元のデスティネーションのタイプに基づいてそれらのコマンドを適切なフレームワーク オペレーションに変換するので、大変便利です。たとえば、アクティビティ デスティネーションで navigate() を呼び出すと、NavControllerstartActivity() を呼び出してくれます。

サンプルの NavHostFragment に関連付けられた NavController オブジェクトを取得する方法はいくつかあります。Kotlin では、フラグメント内、アクティビティ内、ビュー内のどこからナビゲーション コマンドを呼び出すかに応じて、次のいずれかの拡張関数を使用することをおすすめします。

それでは、NavController を使用してナビゲーションを行ってください。[Navigate to Destination] ボタンを連結して、flow_step_one_dest デスティネーション(つまり、FlowStepFragment であるデスティネーション)に移動します。

1. HomeFragment.kt を開きます。

2. onViewCreated()navigate_destination_button を連結します。

HomeFragment.kt

val button = view.findViewById<Button>(R.id.navigate_destination_button)
button?.setOnClickListener {
    findNavController().navigate(R.id.flow_step_one_dest, null)
}

3. アプリを実行して、[Navigate To Destination] ボタンをクリックします。このボタンは flow_step_one_dest デスティネーションにナビゲートします。

クリック リスナーのコードは次のようになります。

val button = view.findViewById<Button>(R.id.navigate_destination_button)
button?.setOnClickListener(
        Navigation.createNavigateOnClickListener(R.id.flow_step_one_dest, null)
)

次に示すように、個々の navigate() 呼び出しには、単純なデフォルトの遷移が関連付けられています。

ユーザーは [Home] から [Navigate To Destination] をクリックして、[Step One] に移動します。

デフォルトの遷移と、呼び出しに関連付けられたその他の属性は、NavOptions のセットを含めることによりオーバーライドできます。NavOptions は、必要なオプションのみをオーバーライドして設定できる Builder パターンを使用します。NavOptions 用の ktx DSL もあります。サンプルではこれを使用しています。

アニメーション化された遷移の場合は、anim リソース フォルダで XML アニメーション リソースを定義して、それらのアニメーションを遷移に使用できます。アプリコードには、次のようなリソースが含まれています。

カスタム遷移を追加する

コードを更新して、[Navigate To Destination] ボタンを押すとカスタム遷移アニメーションが表示されるようにします。

1. HomeFragment.kt を開きます。

2. NavOptions を定義して、navigate_destination_button に対する navigate() 呼び出しに渡します。

val options = navOptions {
    anim {
        enter = R.anim.slide_in_right
        exit = R.anim.slide_out_left
        popEnter = R.anim.slide_in_left
        popExit = R.anim.slide_out_right
    }
}
view.findViewById<Button>(R.id.navigate_destination_button)?.setOnClickListener {
    findNavController().navigate(R.id.flow_step_one_dest, null, options)
}

3. ステップ 5 で追加したコードがまだ残っている場合は、削除します。

4. [Navigate To Destination] ボタンをタップするとフラグメントが別の画面にスライドし、[戻る] ボタンを押すと元の画面にスライドすることを確認します。

アクション

ナビゲーション システムでは、「アクション」を使用したナビゲーションも可能です。前述のように、ナビゲーション グラフに表示される線はアクションを視覚的に表したものです。

デスティネーションによるナビゲーションと比べて、アクションによるナビゲーションには次のような利点があります。

  • アプリ全体のナビゲーション パスを視覚化できる
  • 遷移アニメーション、引数の値、バックスタックの動作など、設定可能な関連属性をアクションに追加できる
  • プラグインの Safe Args をナビゲーションに使用できる(この後すぐ説明します)

flow_step_one_destflow_step_two_dest を接続するアクションの画像と XML を次に示します。

<fragment
    android:id="@+id/flow_step_one_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment">

    <argument
        .../>

    <action
        android:id="@+id/next_action"
        app:destination="@+id/flow_step_two_dest">
    </action>
</fragment>

<fragment
    android:id="@+id/flow_step_two_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment">
    <!-- ...removed for simplicity-->
</fragment>

次の点に注目してください。

  • アクションはデスティネーション内でネストされます。これはナビゲーションの開始デスティネーションです。
  • アクションには、flow_step_two_dest を参照するデスティネーション引数が含まれています。これは移動先の ID です。
  • アクションの ID は「next_action」です。

次の例は、flow_step_two_desthome_dest に接続するもう 1 つのアクションを示しています。

<fragment
    android:id="@+id/home_dest"
    android:name="com.example.android.codelabs.navigation.HomeFragment"
    .../>

<fragment
    android:id="@+id/flow_step_two_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment">

    <argument
        .../>

    <action
        android:id="@+id/next_action"
        app:popUpTo="@id/home_dest">
    </action>
</fragment>

次の点に注目してください。

  • next_action という同じ ID が、flow_step_two_desthome_dest に接続するアクションに使用されています。next_action という ID を使用して、flow_step_one_dest または flow_step_two_dest からナビゲートできます。これは、アクションによって抽象化のレベルを提供し、コンテキストに応じて異なる場所にナビゲートする方法の一例です。
  • popUpTo 属性が使用されています。このアクションは、ユーザーが home_dest に到達するまで、バックスタックからフラグメントをポップオフします。

それでは、[Navigate with Action] ボタンを連結して、その名にふさわしい動作を設定しましょう。

1. mobile_navigation.xml ファイルをデザインモードで開きます。

2. home_dest から flow_step_one_dest に矢印をドラッグします。

3. アクションの矢印(青色で表示)を選択し、アクションのプロパティを次のように変更します。

  • [ID] = next_action
  • [Transition] の [Enter] = slide_in_right
  • [Transition] の [Exit] = slide_out_left
  • [Transition] の [Pop Enter] = slide_in_left
  • [Transition] の [Pop Exit] = slide_out_right

4. [Text] タブをクリックします。

次のように、home_dest デスティネーションの下に next_action アクションが新しく追加されていることを確認します。

mobile_navigation.xml

<fragment android:id="@+id/home_dest"
        ...>

        <action android:id="@+id/next_action"
            app:destination="@+id/flow_step_one"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right" />

5. HomeFragment.kt を開きます。

6. navigate_action_button にクリック リスナーを追加します。

HomeFragment.kt

 view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener(
        Navigation.createNavigateOnClickListener(R.id.next_action, null)
)

7. [Navigate To Action] をタップすると次の画面に移動することを確認します。

Safe Args

Navigation コンポーネントには、Safe Args という Gradle プラグインが含まれています。Safe Args は、デスティネーションとアクション用に指定された引数に対してタイプセーフなアクセスを行うためのシンプルなオブジェクトとビルダークラスを生成します。

Safe Args を使用すると、デスティネーション間で値を渡すときに次のようなコードを使わずに済みます。

val username = arguments?.getString("usernameKey")

このコードは、セッターとゲッターをすでに生成している次のコードに置き換えることができます。

val username = args.username

Safe Args を使用して値を渡す

1. プロジェクトの build.gradle ファイルを開いて、Safe Args プラグインがあることを確認します。

build.gradle

dependencies {
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
    //...
    }

2. app/build.gradle ファイルを開いて、適用されているプラグインを確認します。

app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'androidx.navigation.safeargs.kotlin'

android {
   //...
}

3. mobile_navigation.xml, を開いて、flow_step_one_dest デスティネーションで引数がどのように定義されているかを確認します。

mobile_navigation.xml

<fragment
    android:id="@+id/flow_step_one_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment"
    tools:layout="@layout/flow_step_one_fragment">
    <argument
        android:name="flowStepNumber"
        app:argType="integer"
        android:defaultValue="1"/>

    <action...>
    </action>
</fragment>

safeargs は、<argument> タグを使用して FlowStepFragmentArgs というクラスを生成します。

XML には android:name="flowStepNumber" で指定された flowStepNumber という引数が含まれているため、生成されるクラス FlowStepFragmentArgs にはゲッターとセッターを持つ変数 flowStepNumber が含まれています。

4. FlowStepFragment.kt を開きます。

5. 下記のコード行をコメントアウトします。

FlowStepFragment.kt

// Comment out this line
// val flowStepNumber = arguments?.getInt("flowStepNumber")

この古いスタイルのコードはタイプセーフではありません。Safe Args を使用する方が適切です。

6. FlowStepFragment を更新し、コードで生成されたクラス FlowStepFragmentArgs を使用します。これにより、FlowStepFragment 引数がタイプセーフな方法で取得されます。

FlowStepFragment.kt

val safeArgs: FlowStepFragmentArgs by navArgs()
val flowStepNumber = safeArgs.flowStepNumber

Safe Args の Directions クラス

また、Safe Args を使用すると、引数を追加してもしなくても、タイプセーフな方法でナビゲートできます。そのためには、生成された Directions クラスを使用します。

Directions クラスは、アクションが存在するデスティネーションごとに生成されます。Directions クラスには、デスティネーションに存在する各アクションのメソッドが含まれています。

たとえば、HomeFragment.kt 内の navigate_action_button クリック リスナーは、次のように変更できます。

HomeFragment.kt

// Note the usage of curly braces since we are defining the click listener lambda
view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener {
    val flowStepNumberArg = 1
    val action = HomeFragmentDirections.nextAction(flowStepNumberArg)
    findNavController().navigate(action)
}

Navigation コンポーネントには、NavigationUI クラスと navigation-ui-ktx Kotlin 拡張機能が含まれています。NavigationUI には、メニュー項目をナビゲーション デスティネーションに関連付ける静的メソッドがあります。navigation-ui-ktx は、同じ処理を行う拡張関数のセットです。NavigationUI は、現在のグラフでデスティネーションと同じ ID を持つメニュー項目を検出すると、そのデスティネーションに移動するメニュー項目を構成します。

オプション メニューで NavigationUI を使用する

NavigationUI の最も簡単な使用方法の 1 つは、オプション メニューのセットアップを簡素化することです。NavigationUI は、特に onOptionsItemSelected コールバックの処理を簡素化します。

1. MainActivity.kt を開きます。

onCreateOptionsMenu 内のメニュー overflow_menu をインフレートするコードがすでに存在することを確認します。

2. res/menu/overflow_menu.xml を開きます。

3. オーバーフロー メニューを更新して、settings_dest を含めます。

overflow_menu.xml

<item
    android:id="@+id/settings_dest"
    android:icon="@drawable/ic_settings"
    android:menuCategory="secondary"
    android:title="@string/settings" />

4. MainActivity.kt を開きます。

5. NavigationUI が onNavDestinationSelected ヘルパー メソッドで onOptionsItemSelected を処理するようにします。メニュー項目の目的がナビゲーションでない場合は、super.onOptionsItemSelected で処理します。

MainActivity.kt

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
            || super.onOptionsItemSelected(item)
}

6. アプリを実行します。SettingsFragment に移動する ActionBar メニューが機能することを確認します。

NavigationUI を使用してボトム ナビゲーションを構成する

コードにはボトム ナビゲーションを実装するための XML レイアウト コードがすでに含まれているため、ボトム ナビゲーション バーが表示されます。しかし、それはユーザーをどこにもナビゲートしません。

1. res/layout/navigation_activity/navigation_activity.xml (h470dp) を開いて、[Text] タブをクリックします。

ボトム ナビゲーション用の XML レイアウト コードがあり、bottom_nav_menu.xml を参照していることを確認します。

<com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottom_nav_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:menu="@menu/bottom_nav_menu" />

2. res/menu/bottom_nav_menu.xml を開きます。

ボトム ナビゲーションに 2 つのアイテムがあり、ナビゲーション グラフのデスティネーションとそれらの id が一致していることを確認します。

bottom_nav_menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@id/home_dest"
        android:icon="@drawable/ic_home"
        android:title="@string/home" />
    <item
        android:id="@id/deeplink_dest"
        android:icon="@drawable/ic_android"
        android:title="@string/deeplink" />
</menu>

ボトム ナビゲーションが NavigationUI を使用して実際に機能するようにしましょう。

3. MainActivity.kt を開きます。

4. setupWithNavController(bottomNavigationView: BottomNavigationView, navController: NavController) を使用して setupBottomNavMenu メソッドを実装します。

MainActivity.kt

private fun setupBottomNavMenu(navController: NavController) {
    val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
    bottomNav?.setupWithNavController(navController)
}

これで、ボトム ナビゲーションが機能するようになります。

NavigationUI を使用してナビゲーション ドロワーを構成する

最後に、NavigationUI を使用してサイド ナビゲーションとナビゲーション ドロワーを構成しましょう。これには、ActionBar の処理や、適切な「上へ」ナビゲーションの処理があります。これは、画面が十分に大きい場合や、ボトム ナビゲーションを使用するには画面の縦幅が短すぎる場合に使用されます。

まず、適切なレイアウト XML コードがすでにアプリに存在することを確認します。

1. navigation_activity.xmlnavigation_activity.xml (w960dp) の両方を開きます。

両方のレイアウトに、nav_drawer_menu に接続された NavigationView が含まれていることを確認します。タブレット版(w960dp)では、NavigationView は常に画面に表示されます。小型のデバイスでは、NavigationView は DrawerLayout 内にネストされます。

次に、NavigationView ナビゲーションの実装を開始します。

2. MainActivity.kt を開きます。

3. setupWithNavController(navigationView: NavigationView, navController: NavController) を使用して setupNavigationMenu メソッドを実装します。このバージョンのメソッドは、BottomNavigationView ではなく NavigationView を受け取ることに注意してください。

MainActivity.kt

private fun setupNavigationMenu(navController: NavController) {
    val sideNavView = findViewById<NavigationView>(R.id.nav_view)
    sideNavView?.setupWithNavController(navController)
}

これで、ナビゲーション ビューメニューが画面に表示されますが、ActionBar には影響しません。

ActionBar を設定するには、AppBarConfiguration のインスタンスを作成する必要があります。AppBarConfiguration の目的は、ツールバー、折りたたみツールバー、アクションバーに必要な構成オプションを指定することです。構成オプションでは、バーがドロワー レイアウトを処理する必要があるかどうかや、どのデスティネーションをトップレベル デスティネーションと見なすかなどを指定します。

トップレベル デスティネーションは、アプリのルートレベル デスティネーションです。これらのデスティネーションはアプリバーに [上へ] ボタンを表示せず、デスティネーションがドロワー レイアウトを使用している場合はドロワー アイコンを表示します。

4. トップレベル デスティネーション ID とドロワー レイアウトのセットを渡して、AppBarConfiguration を作成します。

MainActivity.kt

val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout)
appBarConfiguration = AppBarConfiguration(
        setOf(R.id.home_dest, R.id.deeplink_dest),
        drawerLayout)

AppBarConfiguration を作成したので、NavigationUI.setupActionBarWithNavController を呼び出すことができます。これにより、次の処理が行われます。

  • デスティネーションのラベルに基づいて ActionBar にタイトルを表示する
  • ユーザーがトップレベル デスティネーションにいないときは、常に [上へ] ボタンを表示する
  • ユーザーがトップレベル デスティネーションにいるときは、ドロワー アイコン(ハンバーガー アイコン)を表示する

5. setupActionBarWithNavController を実装します。

MainActivity.kt

private fun setupActionBar(navController: NavController,
                           appBarConfig : AppBarConfiguration) {
    setupActionBarWithNavController(navController, appBarConfig)
}

また、[上へ] ボタンが押されたときに起こることを NavigationUI に処理させる必要があります。

6. onSupportNavigationUp をオーバーライドし、同じ AppBarConfiguration を使用して NavigationUI.navigateUp を呼び出します。

MainActivity.kt

override fun onSupportNavigateUp(): Boolean {
    return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
}

7. コードを実行します。分割画面でアプリを開くと、機能するナビゲーション ドロワーが表示されることを確認します。「上へ」アイコンとドロワー アイコンは、適切なタイミングで表示され、正常に動作するはずです。

NavigationView への新しいデスティネーションは簡単に追加できます。「上へ」と「戻る」のナビゲーションで機能するナビゲーション ドロワーを作成した後は、新しいメニュー項目を追加するだけです。

8. menu/nav_drawer_menu.xml を開きます。

9. settings_dest に対応する新しいメニュー項目を追加します。

<item
    android:id="@+id/settings_dest"
    android:icon="@drawable/ic_settings"
    android:title="@string/settings" />

これで、ナビゲーション ドロワーに [設定] 画面がデスティネーションとして表示されます。よくできました。

Navigation コンポーネントは、ディープリンクもサポートしています。ディープリンクは、実際の URL リンクまたは通知のペンディング インテントから、アプリのナビゲーションの途中にジャンプする方法です。

Navigation ライブラリを使用してディープリンクを処理する利点の 1 つは、アプリ ウィジェット、通知、ウェブリンクなど、他のエントリ ポイントからの適切なバックスタックで、適切なデスティネーションからナビゲーションを開始できることです(次のステップで説明します)。

Navigation ライブラリには、ユーザーを特定のデスティネーションにナビゲートする PendingIntent を作成するための NavDeepLinkBuilder クラスが用意されています。

NavDeepLinkBuilder を使用して、アプリ ウィジェットをデスティネーションに連結します。

1. DeepLinkAppWidgetProvider.kt を開きます。

2. NavDeepLinkBuilder で作成した PendingIntent を追加します。

DeepLinkAppWidgetProvider

val args = Bundle()
args.putString("myarg", "From Widget");
val pendingIntent = NavDeepLinkBuilder(context)
        .setGraph(R.navigation.mobile_navigation)
        .setDestination(R.id.deeplink_dest)
        .setArguments(args)
        .createPendingIntent()

remoteViews.setOnClickPendingIntent(R.id.deep_link_button, pendingIntent)

次の点に注目してください。

  • setGraph には、ナビゲーション グラフが含まれています。
  • setDestination は、リンクの移動先を指定しています。
  • setArguments には、ディープリンクに渡す引数が含まれています。

3. ホーム画面にディープリンク ウィジェットを追加します。ホーム画面を長押しすると、ウィジェットを追加するオプションが表示されます。

長押しする

下にスクロールしてウィジェットを探す

完了すると、ディープリンク ウィジェットが表示されます。

4. ウィジェットをタップして、Android デスティネーションが正しい引数で開くことを確認します。上部に「From Widget」と表示されます。これは、DeepLinkAppWidgetProvider で渡した引数です。

5. [戻る] ボタンを押すと home_dest デスティネーションに移動することを確認します。

ディープリンクのバックスタックは、渡されたナビゲーション グラフによって決定されます。選択した明示的なアクティビティに親アクティビティがある場合は、それらの親アクティビティも含まれます。

バックスタックは、app:startDestination で指定されたデスティネーションを使用して生成されます。このアプリには 1 つのアクティビティと 1 つのナビゲーション レベルしかないため、バックスタックはユーザーを home_dest デスティネーションにナビゲートします。

もっと複雑なナビゲーションには、ネストされたナビゲーション グラフが含まれる場合があります。ネストされたグラフでは、各レベルの app:startDestination によってバックスタックが決定されます。ディープリンクとネストされたグラフについて詳しくは、ナビゲーションの原則をご覧ください。

ディープリンクの最も一般的な用途の 1 つは、ウェブリンクがアプリ内のアクティビティをオープンできるようにすることです。従来は、インテント フィルタを使用して、オープンするアクティビティに URL を関連付けていました

Navigation ライブラリを使用すると、この処理を著しく簡素化して、ナビゲーション グラフのデスティネーションに URL を直接マッピングできます。

<deepLink> は、グラフ内のデスティネーションに追加できる要素です。各 <deepLink> 要素には、単一の必須属性 app:uri があります。

URI を直接マッチングする機能に加えて、次の機能がサポートされています。

  • スキーマのない URI は、http および https と見なされます。たとえば、www.example.comhttp://www.example.com および https://www.example.com と一致します。
  • {placeholder_name} という形式のプレースホルダを使用して、1 つ以上の文字に一致させることができます。プレースホルダの文字列値は、同じ名前のキーを持つ引数バンドルで使用できます。たとえば、http://www.example.com/users/{id}http://www.example.com/users/4 と一致します。
  • ワイルドカード .* は、ゼロ個以上の文字に一致させることができます。
  • NavController は自動的に ACTION_VIEW インテントを処理して、一致するディープリンクを探します。

このステップでは、www.example.com にディープリンクを追加します。

1. mobile_navigation.xml を開きます。

2. deeplink_dest デスティネーションに <deepLink> 要素を追加します。

mobile_navigation.xml

<fragment
    android:id="@+id/deeplink_dest"
    android:name="com.example.android.codelabs.navigation.DeepLinkFragment"
    android:label="@string/deeplink"
    tools:layout="@layout/deeplink_fragment">

    <argument
        android:name="myarg"
        android:defaultValue="Android!"/>

    <deepLink app:uri="www.example.com/{myarg}" />
</fragment>

3. AndroidManifest.xml を開きます。

4. nav-graph タグを追加します。これにより、適切なインテント フィルタが生成されます。

AndroidManifest.xml

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <nav-graph android:value="@navigation/mobile_navigation" />
</activity>

5. ディープリンクを使用してアプリを起動します。これには、次の 2 つの方法があります。

  • 次のように adb を使用します。
adb shell am start -a android.intent.action.VIEW -d "http://www.example.com/urlTest" 
  • Google アプリでナビゲートします。www.example.com/urlTest を検索バーに入力すると、曖昧さ回避ウィンドウが表示されます。[Navigation codelab] を選択します。

検索バーを使用して開く

(Chrome 以外)

曖昧さ回避ダイアログ

いずれの場合も、画面に「urlTest」というメッセージが表示されます。これは、URL からフラグメントに渡された文字列です。

Codelab アプリには、演習対象の機能がもう 1 つあります。それはショッピング カート ボタンです。

この演習は、この Codelab で学習したスキルの総まとめです。このステップにはコメントがないので、独力で以下に挑戦してください。

  1. 新しいフラグメント クラスを作成する
  2. フラグメントをデスティネーションとしてナビゲーション グラフに追加する
  3. メニューを処理する NavigationUI を使用して、ショッピング カート アイコンが新しいフラグメント クラスを開くようにする

以上で、Navigation コンポーネントを支える基本的なコンセプトを習得しました。この Codelab で学習した内容は次のとおりです。

  • ナビゲーション グラフの構造
  • NavHostFragment と NavController
  • 特定のデスティネーションに移動する方法
  • アクションによるナビゲーション
  • デスティネーション間で引数を渡す方法(新しい safeargs プラグインの使い方を含む)
  • メニュー、ボトム ナビゲーション、ナビゲーション ドロワーによるナビゲーション
  • ディープリンクによるナビゲーション


このアプリを引き続き利用することも、ご自身のアプリでナビゲーションを使用することもできます。

次に示すように、試す価値があることは他にも数多くあります。

  • バックスタック(またはバックスタック操作)からデスティネーションをポップする
  • ネストされたナビゲーション グラフ
  • 条件付きナビゲーション
  • 新しいデスティネーションのサポートの追加

Navigation コンポーネントについて詳しくは、ドキュメントをご覧ください。その他のアーキテクチャ コンポーネントについてさらに学習するには、次の Codelab をお試しください。