【和訳】Jetpack Navigation(Codelabs for the 2019 Android Dev Summit: Mountain View, October 23-24, 2019.)

[Android Dev Summit 2019
Codelabs for the 2019 Android Dev Summit: Mountain View, October 23-24, 2019.](https://codelabs.developers.google.com/ads19/)

[Jetpack Navigation](https://codelabs.developers.google.com/codelabs/android-navigation/index.html?index=..%2F..ads19)の日本語訳

# 1.はじめに
[ナビゲーションアーキテクチャコンポーネント](http://d.android.com/arch/navigation)は、ナビゲーションの実装を簡素化すると同時に、アプリのナビゲーションフローを視覚化するのにも役立ちます。 ライブラリには、次のような多くの利点があります。

- フラグメントトランザクションの自動処理
- デフォルトで進むと戻るの適切な処理
- アニメーションと遷移のデフォルト動作
- ファーストクラスの操作としてのディープリンク
- 追加の作業をほとんど必要としないナビゲーションUIパターン（ナビゲーションドロワーやボトムナビゲーションなど）の実装
- ナビゲート中に情報を渡すときの型安全
- アプリのナビゲーションフローを視覚化および編集するためのAndroid Studioツール

## 構築するもの
このコードラボでは、以下に示すサンプルアプリを使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/99da2db4-286e-85cc-babd-d171458e993b.png)

すべてのアクティビティとフラグメントはすでに作成されています。 ナビゲーションコンポーネントを使用してそれらを接続し、その際に以下を実装します。

- 視覚的なナビゲーショングラフ
- 宛先とアクションによるナビゲーション
- 遷移アニメーション
- メニューナビゲーション、ボトムナビゲーション、メニュードロワーナビゲーション
- 型安全な引数の受け渡し
- ディープリンク

## 前提条件

- Kotlinの基本的な知識（このコードラボはKotlinにあります）
- Android Studio 3.2以降
- API 14以上を実行可能なエミュレーターまたはデバイス

# 2.開始
コードを入手する
GitHubからナビゲーションコードラボを複製します。

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

または、リポジトリを[Zipファイル](https://github.com/googlecodelabs/android-navigation/archive/master.zip)としてダウンロードできます。

## Android Studio 3.3以降を入手してください
Android Studio 3.3以降を使用していることを確認してください。 これは、Android Studioのナビゲーションツールに必要です。
Android Studioの最新バージョンをダウンロードする必要がある場合は、[ここ](https://developer.android.com/studio/)からダウンロードできます。

## ナビゲーションの概要
ナビゲーションコンポーネントは3つの主要な部分で構成され、調和して連携して機能します。 それらです：
1. **ナビゲーショングラフ**（新しいXMLリソース） - これは、1つの場所にすべてのナビゲーション関連情報を含むリソースです。これには、目的地と呼ばれるアプリ内のすべての場所と、ユーザーがアプリを経由する可能性のあるパスが含まれます。
2. **NavHostFragment**（レイアウトXMLビュー） - これは、レイアウトに追加する特別なウィジェットです。ナビゲーショングラフのさまざまな目的地が表示されます。
3. **NavController**（Kotlin / Javaオブジェクト） - これは、ナビゲーショングラフ内の現在の位置を追跡するオブジェクトです。ナビゲーショングラフを移動するときに、NavHostFragment内の宛先コンテンツの交換を調整します。

ナビゲートするときは、NavControllerオブジェクトを使用して、ナビゲーショングラフでどこに行きたいか、どのパスをたどるかを伝えます。`NavController`は、NavHostFragmentに適切な宛先を表示します。

それが基本的な考え方です。 新しいNavigation Graphリソースから始めて、これが実際にどのように見えるかを見てみましょう。

# 3.ナビゲーショングラフの紹介
## 宛先
ナビゲーションコンポーネントは、目的地の概念を導入します。 宛先とは、アプリ内でナビゲートできる任意の場所で、通常はフラグメントまたはアクティビティです。これらはデフォルトでサポートされていますが、必要に応じて独自のカスタム宛先タイプを作成することもできます。

## ナビゲーショングラフ
*ナビゲーショングラフ*は、ユーザーがアプリを通過できるすべての可能なパスを定義する新しいリソースタイプです。特定の宛先から到達可能なすべての宛先を視覚的に表示します。Android Studioのナビゲーションエディターにグラフが表示されます。 アプリ用に作成する開始ナビゲーショングラフの一部を次に示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/c45b8bb6-f989-c1ac-ce99-efcf0bbd4d8c.png)

## ナビゲーションエディターの詳細

1.res/navigation/mobile_navigation.xmlを開きます
2.[デザイン]をクリックしてデザインモードに入ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/3ce450f2-23a4-76ce-9206-8e382d11d4cb.png)

表示される内容は次のとおりです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/c5344273-b0ed-6c3b-845e-e98f2ed9fd4f.png)

ナビゲーショングラフには、利用可能な目的地が表示されます。 宛先間の矢印はアクションと呼ばれます。 アクションの詳細については後で学びます。

3.宛先をクリックして、その属性を表示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/403e27cf-64f8-2c1f-6bd0-b269d9e7a96b.png)

4.矢印で表されているアクションをクリックして、その属性を表示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/3e1e3463-14b6-0756-d27e-cb00ed2bab38.png)

## ナビゲーションXMLファイルの構造
グラフィカルナビゲーションエディターで行うすべての変更は、レイアウトエディターがレイアウトXMLを変更する方法と同様に、基になるXMLファイルを変更します。

[テキスト]タブをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/21b86598-e3ef-1c60-dc21-72d171ba25ef.png)

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

```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>`には、`<activity>`または`<fragment>`要素で表される1つ以上の宛先が含まれます。
- `app：startDestination`は、ユーザーが最初にアプリを開いたときにデフォルトで起動される宛先を指定する属性です。

フラグメントの宛先を見てみましょう。

```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
        .../>

    <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>`も含まれています。これらのすべてについては後で説明します。

# 4.ナビゲーショングラフに目的地を追加する
サンプルアプリは、グラフ内のいくつかの目的地から始まります。 このステップでは、新しい目的地を追加します！ ナビゲーショングラフに移動するには、ナビゲーショングラフに移動先を追加する必要があります。

> **注**：このコードラボの各ステップのコードは含まれており、ダウンロードしたコードのTODOステートメントの間にコメントアウトされています。
> 
> 記述したコードを、含まれているコメントアウトされたコードと比較する必要があります。

1.res/navigation/mobile_navigation.xmlを開き、[デザイン]タブをクリックします。
2.**新規宛先**アイコンをクリックして、「settings_fragment」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/e8f043d9-fef4-1778-d50f-c9605f75843d.png)

その結果、デザインビューでフラグメントのレイアウトのプレビューをレンダリングする新しい宛先が作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/eb45a9c6-d1e7-da68-ef14-7e8f95d7061b.png)

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" />
```

> 命名規則に従うには、idをデフォルトの`settingsFragment`から`settings_dest`に変更します。

# 5.ナビゲーショングラフを使用してナビゲートする
今、あなたはこの素晴らしいナビゲーショングラフを持っていますが、実際にそれを使ってナビゲートしているわけではありません。

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

これに対して、フラグメントは実際の宛先固有のレイアウトになります。

これをすべて機能させるには、アクティビティレイアウトを変更して、[NavHostFragment](https://developer.android.com/reference/androidx/navigation/fragment/NavHostFragment)と呼ばれる特別なウィジェットを含める必要があります。`NavHostFragment`は、ナビゲーショングラフをナビゲートするときに、さまざまなフラグメントの宛先を入出力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/2e3b1aef-f8ac-c89a-e5cc-85d3fb2a8931.png)

上の図のようなナビゲーションをサポートするシンプルなレイアウトは次のようになります。このコードの例は、`res/layout-470dp/navigation_activity.xml`にあります。

```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
最後に、ユーザーがボタンをクリックするなどの操作を行う場合、ナビゲートコマンドをトリガーする必要があります。[NavController](https://developer.android.com/reference/androidx/navigation/NavController)と呼ばれる特別なクラスは、`NavHostFragment`のフラグメントスワップをトリガーします。

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

ナビゲートする宛先またはアクションIDを渡すことに注意してください。これらは、ナビゲーショングラフXMLで定義されているIDです。これは、宛先IDを渡す例です。

`NavController`は強力です。なぜなら、`navigate（）`や`popBackStack（）`などのメソッドを呼び出すと、これらのコマンドは、ナビゲート先のタイプに基づいて適切なフレームワーク操作に変換されます。たとえば、アクティビティの宛先で`navigate（）`を呼び出すと、`NavController`が代わりに`startActivity（）`を呼び出します。

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

- [Fragment.findNavController()](https://developer.android.com/reference/kotlin/androidx/navigation/fragment/package-summary#findnavcontroller)
- [View.findNavController()](https://developer.android.com/reference/kotlin/androidx/navigation/package-summary#%28android.view.View%29.findNavController%28%29)
- [Activity.findNavController(viewId: Int)](https://developer.android.com/reference/kotlin/androidx/navigation/package-summary#findnavcontroller)

> `NavController`は`NavHostFragment`に関連付けられています。したがって、どの方法を使用する場合でも、フラグメント、ビュー、またはビューIDが`NavHostFragment`自体であるか、親として`NavHostFragment`を持っていることを確認する必要があります。そうしないと、`IllegalStateException`が発生します。

## NavControllerを使用して宛先に移動する
`NavController`を使用してナビゲートする番です。**宛先にナビゲート**ボタンをフックして、`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.アプリを実行し、**移動先へ移動**ボタンをクリックします。 ボタンが`flow_step_one_dest`宛にナビゲートすることに注意してください。

> 便利なメソッド[Navigation.createNavigateOnClickListener（@IdRes destId：int、bundle：Bundle）](https://developer.android.com/reference/kotlin/androidx/navigation/Navigation#createNavigateOnClickListener%28kotlin.Int%2C+android.os.Bundle%29)を使用することもできます。このメソッドは、`OnClickListener`を構築して、宛先に渡される引数のバンドルを使用して、指定された宛先にナビゲートします。

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

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

# 6.ナビゲーション遷移の変更
以下に示すように、各`navigate（）`呼び出しには、それほど刺激的なデフォルト遷移が関連付けられていません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/ab44c2da-1078-6001-945e-fc8d19f1e1e8.png)

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

アニメーション化されたトランジションの場合、`anim`リソースフォルダーでXMLアニメーションリソースを定義し、それらのアニメーションをトランジションに使用できます。アプリコードにはいくつかの例が含まれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/ebb45139-bd74-dd08-35b6-5a96f0472e6a.png)

## カスタム遷移を追加する
**宛先へ移動**ボタンを押すとカスタムの遷移アニメーションが表示されるようにコードを更新します。

1.HomeFragment.ktを開きます
2.`NavOptions`を定義し、`navigate_destination_button`の`navigate（）`呼び出しに渡します

```kotlin
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.**移動先へ移動**ボタンをタップするとフラグメントが画面上にスライドし、バックキーを押すと画面外にスライドすることを確認します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/31709c65-a531-6715-c054-02d842df47f4.png)

# 7.アクションを使用してナビゲートする
## Actions
ナビゲーションシステムでは、アクションを介してナビゲートすることもできます。前述のように、ナビゲーショングラフに表示される線は、アクションの視覚的表現です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/48954cc5-0c06-a94a-3fd9-a18c5e20f77c.png)


アクションによるナビゲーションには、目的地によるナビゲーションよりも次の利点があります。

- アプリのナビゲーションパスを視覚化できます
- アクションには、遷移アニメーション、引数値、バックスタック動作など、設定可能な追加の関連属性を含めることができます
- プラグインで安全な引数を使用してナビゲートできます

`flow_step_one_dest`と`flow_step_two_dest`を接続するアクションのビジュアルとXMLは次のとおりです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/2968c954-9289-42a4-30b1-b31610e31652.png)

```xml
<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>
```

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

## アクションでナビゲートする
**名前を付けてナビゲート**ボタンを接続して、その名前どおりになるようにします。

1.**デザイン**モードで`mobile_navigation.xml`ファイルを開きます
2.`home_dest`から`flow_step_one_dest`に矢印をドラッグします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/915325d0-f4c3-1ad3-962c-1c861fb9f1af.png)

3.アクションの矢印が選択された状態（青）で、アクションのプロパティを次のように変更します。

- ID = next_action
- Transition for Enter = slide_in_right
- Transition for Exit = slide_out_left
- Transitions for Pop Enter = slide_in_left
- Transitions for Pop Exit = slide_out_right

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/4a63bd73-3873-f7ec-c522-e6d724baf2b9.png)

4.**テキスト**タブをクリックします
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)
)
```

> アクションを使用すると、プログラムで指定するのではなく、ナビゲーションXMLファイルに`NavOptions`を添付できます。

7.**操作に移動**をタップすると、次の画面に移動することを確認します。

# 8.ナビゲーションに安全な引数を使用する
## Safe Args
ナビゲーションコンポーネントには、[Safe Args](https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args)と呼ばれるGradleプラグインがあり、目的地とアクションに指定された引数へのタイプセーフなアクセスのためのシンプルなオブジェクトとビルダークラスを生成します。

Safe argsを使用すると、宛先間で値を渡すときに次のようなコードを取り除くことができます。

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

そして、代わりに、セッターとゲッターを生成したコードに置き換えます。

```kotlin
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>
```

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/ba0ed33c-95d3-3127-9218-85b3f87a14fd.png)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/a8451054-ddfc-d179-daa6-f52e169da7bb.png)

4.`FlowStepFragment.kt`を開きます
5.以下に示すコード行をコメントアウトします。

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

この古いスタイルのコードはタイプセーフではありません。 安全な引数を使用することをお勧めします。

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

```FlowStepFragment.kt
val safeArgs: FlowStepFragmentArgs by navArgs()
val flowStepNumber = safeArgs.flowStepNumber
```

## Safe Args Directionクラス
引数を追加してもしなくても、安全な引数を使用して型安全な方法でナビゲートすることもできます。これは、生成されたDirectionsクラスを使用して行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/4e00d334-7347-fe59-f88c-918746fb7f81.png)

Directionsクラスは、アクションを持つすべての個別の宛先に対して生成されます。Directionsクラスには、目的地のすべてのアクションのメソッドが含まれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/2e0270f2-82e0-3b06-1249-b896237fea1d.png)

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

```HomeFragment.kt
// クリックリスナーラムダを定義しているため、中括弧の使用に注意してください
view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener {
    val flowStepNumberArg = 1
    val action = HomeFragmentDirections.nextAction(flowStepNumberArg)
    findNavController().navigate(action)
}
```

> ナビゲーショングラフXMLでは、各引数に`defaultValue`を提供できることに注意してください。そうしない場合は、次に示すように、アクションに引数を渡す**必要**があります。
> 
> `HomeFragmentDirections.nextAction(flowStepNumberArg)`

# 9.メニュー、ドロワー、ボトムナビゲーションを使用したナビゲーション
## NavigationUIおよびnavigation-ui-ktx
ナビゲーションコンポーネントには、[NavigationUI](https://developer.android.com/reference/androidx/navigation/ui/NavigationUI)クラスと[navigation-ui-ktx](https://developer.android.com/reference/kotlin/androidx/navigation/ui/package-summary#extension-functions-summary) 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`で`onOptionsItemSelected`を[onNavDestinationSelected](https://developer.android.com/reference/kotlin/androidx/navigation/ui/NavigationUI#onNavDestinationSelected%28android.view.MenuItem%2C+NavController%29)ヘルパーメソッドで処理します。メニュー項目がナビゲートすることを意図していない場合は、`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メニューが必要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/2a82f5e4-f74f-210e-c75e-0ee749918e8e.png)

## NavigationUIを使用してボトムナビゲーションを構成する
コードには、ボトムナビゲーションを実装するためのXMLレイアウトコードが既に含まれているため、ボトムナビゲーションバーが表示されます。 しかし、どこにも移動しません。

1.`res/layout/navigation_activity/navigation_activity.xml（h470dp）`を開き、**テキスト**タブをクリックします
ボトムナビゲーションのXMLレイアウトコードが存在し、`bottom_nav_menu.xml`を参照していることに注意してください。

```navigation_activity.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）](https://developer.android.com/reference/kotlin/androidx/navigation/ui/NavigationUI#setupwithnavcontroller_2)を使用して`setupBottomNavMenu`メソッドを実装します。

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

これで、ボトムナビゲーションが機能します！

## NavigationUIを使用してナビゲーションドロワーを構成する
最後に、`NavigationUI`を使用してサイドナビゲーションとナビゲーションドロワーを構成します。これにはActionBarのと[proper up navigation](https://developer.android.com/guide/navigation/navigation-principles#the_up_button_never_exits_your_app)の処理を含みます。これは、画面が十分に大きい場合、または画面が下のナビゲーションには短すぎる場合に表示されます。

最初に、適切なレイアウトXMLコードが既にアプリにどのようにあるかを観察します。

1.`navigation_activity.xml`と`navigation_activity.xml (w960dp)`を開きます
両方のレイアウトにnav_drawer_menuに接続されたNavigationViewが含まれていることに注意してください。タブレットバージョン（w960dp）では、NavigationViewは常に画面に表示されます。小さいデバイスでは、NavigationViewはDrawerLayout内にネストされます。

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

2.MainActivity.ktを開く
3.[setupWithNavController（navigationView：NavigationView、navController：NavController）](https://developer.android.com/reference/kotlin/androidx/navigation/ui/NavigationUI#setupwithnavcontroller)を使用して`setupNavigationMenu`メソッドを実装します。
このバージョンのメソッドは、`BottomNavigationView`ではなく`NavigationView`をどのように使用するかに注意してください。

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

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

`ActionBar`をセットアップするには、[AppBarConfiguration](https://developer.android.com/reference/androidx/navigation/ui/AppBarConfiguration)のインスタンスを作成する必要があります。
`AppBarConfiguration`の目的は、ツールバー、折りたたみツールバー、およびアクションバーに必要な構成オプションを指定することです。
構成オプションには、バーが引き出しレイアウトを処理する必要があるかどうか、どの宛先が**最上位の宛先**と見なされるかなどが含まれます。

トップレベルの宛先は、アプリのルートレベルの宛先です。これらの宛先は、アプリバーに「上」ボタンを表示せず、宛先がドロワーレイアウトを使用する場合、ドロワーアイコンを表示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/b7303016-fd7e-8273-e61f-1a5073c6596a.png)

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)
```

> **トップレベルの宛先を決定する方法**
> 
> ボトムナビゲーションやサイドナビゲーションなど、グローバルナビゲーションUIを介して到達可能な宛先はすべて、階層の同じトップレベルにあるようにユーザーに表示されます。したがって、これらは最上位の宛先です。 `home_dest`および`deeplink_dest`は下のナビゲーションにあり、これら両方の宛先にドロワーアイコンを表示するため、これらは最上位の宛先です。
> 開始宛先は常に最上位の宛先と見なされることに注意してください。 最上位の宛先のリストを指定しない場合、唯一の最上位の宛先は開始宛先です。[AppBarConfiguration](https://developer.android.com/guide/navigation/navigation-ui#appbarconfiguration)の詳細については、ドキュメントをご覧ください。

AppBarConfigurationができたので、[NavigationUI.setupActionBarWithNavController](https://developer.android.com/reference/kotlin/androidx/navigation/ui/NavigationUI#setupactionbarwithnavcontroller_1)を呼び出すことができます。これにより、次のことが行われます。

- 宛先のラベルに基づいてActionBarにタイトルを表示します
- 最上位の目的地にいないときはいつでも、上ボタンを表示する
- トップレベルの目的地にいるときに引き出しアイコン（ハンバーガーアイコン）を表示する

5.`setupActionBarWithNavController`を実装する

```MainActivity.kt
private fun setupActionBar(navController: NavController,
                           appBarConfig : AppBarConfiguration) {
    setupActionBarWithNavController(navController, appBarConfig)
}
```

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

6.`onAppNNavigationUp`をオーバーライドし、同じ`AppBarConfiguration`を使用して[NavigationUI.navigateUp](https://developer.android.com/reference/kotlin/androidx/navigation/ui/package-summary.html#navigateup_1)を呼び出します。

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

> いくつかの異なる`navigateUp`メソッドがあることに注意してください。 `AppBarConfiguration`パラメーターを受け入れる[Navigation UI](https://developer.android.com/reference/kotlin/androidx/navigation/ui/package-summary.html#navigateup_1)から次のインポートを使用していることを確認してください。
> `import androidx.navigation.ui.navigateUp`

7.コードを実行します。 分割画面でアプリを開くと、ナビゲーションドロワーが機能するはずです。 適切なタイミングで上アイコンと引き出しアイコンが表示され、正しく機能するはずです。

> レイアウト**navigation_activity.xml（h470dp）**は、ポートレートモードの電話機で使用されます。このレイアウトにはナビゲーションドロワーは含まれず、代わりにボトムナビゲーションが含まれます。そのため、ナビゲーションドロワーを表示するにはアプリを分割画面で開く必要があります。ナビゲーションドロワーとボトムナビゲーションの両方を備えたレイアウトがない理由は、マテリアルデザインガイドラインが[これに対して警告している](https://material.io/components/navigation-drawer/#usage)ためです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/c009ded6-a561-f956-48b5-05d7ad083670.png)

NavigationViewへの新しい目的地の追加は簡単です。 ナビゲーションドロワーで上下のナビゲーションを操作したら、新しいメニュー項目を追加するだけです。

8.`menu/nav_drawer_menu.xml`を開く
9.settings_destの新しいメニュー項目を追加します

```menu/nav_drawer_menu.xml
<item
    android:id="@+id/settings_dest"
    android:icon="@drawable/ic_settings"
    android:title="@string/settings" />
```

これで、ナビゲーションドロワーに設定画面が目的地として表示されます。 よくできました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/00582c7b-3c69-05e2-1e9a-06bd453b71a4.png)

# 10.宛先へのディープリンク
## ディープリンクとナビゲーション
ナビゲーションコンポーネントには、ディープリンクのサポートも含まれています。ディープリンクは、実際のURLリンクからのものであれ、通知からの保留中の意図であれ、アプリのナビゲーションの途中にジャンプする方法です。

ナビゲーションライブラリを使用してディープリンクを処理する利点の1つは、ユーザーがアプリウィジェット、通知、またはWebリンク（次の手順で説明）などの他のエントリポイントから適切なバックスタックを使用して適切な目的地で開始できることです。

ナビゲーションは、ユーザーを特定の目的地に導くPendingIntentを構築するNavDeepLinkBuilderクラスを提供します。

## ディープリンクを追加する
`NavDeepLinkBuilder`を使用して、アプリウィジェットを宛先に接続します。

1.`DeepLinkAppWidgetProvider.kt`を開く
2.`NavDeepLinkBuilder`で構築された`PendingIntent`を追加します。

```DeepLinkAppWidgetProvider.kt
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`には、ディープリンクに渡す引数が含まれます。

> デフォルトでは、`NavDeepLinkBuilder`はランチャーアクティビティを開始します。 アクティビティをコンテキストとして渡すか、`setComponentName（）`を介して明示的なアクティビティクラスを設定することにより、この動作をオーバーライドできます。

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/eddf10cc-592d-03a4-a002-fe1ca5a8dc55.png)
タップ＆ホールド

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/ee169713-c4ad-150e-0883-e10f633f8548.png)
下にスクロールしてウィジェットを見つけます

完了すると、ディープリンクウィジェットが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/6a233136-5390-f9fb-979d-2d609733eae9.png)

4.ウィジェットをタップし、Androidの宛先が正しい引数で開くことを確認します。 これは、`DeepLinkAppWidgetProvider`で渡した引数であるため、上部に「From Widget」と表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/f59aede4-d001-6670-79ef-a18c68dd3fdd.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/ee2d60d1-b686-54b0-cf02-2a0e45f78a2e.png)

5.戻るボタンを押すと、`home_dest`宛先に移動することを確認します。

> 便宜上、`NavController`の`createDeepLink（）`メソッドを呼び出して、`NavController`のコンテキストと現在のナビゲーショングラフを使用することもできます。

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

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

より複雑なナビゲーションには、ネストされたナビゲーショングラフを含めることができます。ネストされたグラフの各レベルで`app：startDestination`がバックスタックを決定します。ディープリンクとネストされたグラフの詳細については、[ナビゲーションの原則](https://developer.android.com/guide/navigation/navigation-principles)をご覧ください。

# 11. Webリンクとリンク先の関連付け
## <deepLink>要素
ディープリンクの最も一般的な用途の1つは、Webリンクがアプリでアクティビティを開くことを許可することです。従来は、[intent-filterを使用して、URLを開きたいアクティビティに関連付けていました](https://developer.android.com/training/app-links/deep-linking#adding-filters)。

ナビゲーションライブラリを使用すると、これが非常に簡単になり、URLをナビゲーショングラフの目的地に直接マッピングできます。

`<deepLink>`は、グラフの宛先に追加できる要素です。各`<deepLink>`要素には、単一の必須属性`app：uri`があります。
URIの直接一致に加えて、次の機能がサポートされています。

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

## <deepLink>を使用してURIベースのディープリンクを追加する
この手順では、`www.example.com`へのディープリンクを追加します。

1.mobile_navigation.xmlを開く
2.`<deepLink>`要素を`deeplink_dest`宛先に追加します。

```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>
```

> 生成されたものを確認したい場合は、出力APKで結果を見つけることができます。
> プロジェクトビューで、**app -> build -> outputs -> apk ->debug -> app-debug.apk**に移動します。
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/067ead53-e65a-576b-7154-e0df1861dab4.png)
> 
> **app-debug.apk**をダブルクリックして[APKアナライザー](https://developer.android.com/studio/build/apk-analyzer)で開きます。 ここでは、生成されたAndroidManifestを見ることができます。

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

- adbを使う：

```bash
adb shell am start -a android.intent.action.VIEW -d "http://www.example.com/urlTest" 
```

- Googleアプリ経由で移動します。`www.example.com/urlTest`を検索バーに配置すると、曖昧さ回避ウィンドウが表示されます。**ナビゲーションコードラボ**を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/2af591f5-e815-1537-9b24-a5f4d1a72ea8.png)
検索バーを使用して開いた（Chromeではない）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/47e497c2-4424-6e39-606c-0de6603e9894.png)
Disambiguationダイアログ

いずれにしても、画面に「urlTest」というメッセージが表示されるはずです。 これは、URLからフラグメントに渡されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/9a49f3f4-0a5c-9036-163b-ad96735b0bf6.png)

# 12.（オプション）自分でナビゲートする
コードラボアプリには、試してみるためのもう1つの部分があります。それがショッピングカートボタンです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/66baba8d-f47d-ab62-b557-9678235f4e61.png)

これは、このコードラボで学んだスキルの要約です。この手順にはコメントが含まれていないため、自分で試してみてください。

1. 新しいフラグメントクラスを作成する
2. フラグメントをナビゲーショングラフの宛先として追加します
3. NavigationUIを使用してメニューを処理し、ショッピングカートアイコンで新しいフラグメントクラスを開きます。

# 13.おめでとうございます！
ナビゲーションコンポーネントの背後にある基本概念に精通している！ このコードラボでは、次のことを学びました。

- ナビゲーショングラフ構造
- NavHostFragmentおよびNavController
- 特定の目的地に移動する方法
- アクションでナビゲートする方法
- 新しいsafeargsプラグインの使用を含む、宛先間で引数を渡す方法
- メニュー、ボトムナビゲーション、ナビゲーションドロワーを使用したナビゲーション
- ディープリンクを介したナビゲーション

このアプリを引き続き使用するか、[独自のアプリ](https://developer.android.com/topic/libraries/architecture/adding-components#navigation)でナビゲーションを使用してください。

試してみるべきことは他にもたくさんあります。

- バックスタックからの宛先のポップ（またはバックスタック操作）
- ネストされたナビゲーショングラフ
- 条件付きナビゲーション
- 新しい宛先のサポートを追加する

ナビゲーションコンポーネントの詳細については、[ドキュメント](https://developer.android.com/guide/navigation/)をご覧ください。他の[アーキテクチャコンポーネント](https://developer.android.com/topic/libraries/architecture)について学びたい場合は、次のコードラボを試してください。

- [Room with a View Codelab](https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#0) (LiveData, ViewModel and Room)
- [Android WorkManager Codelab](https://codelabs.developers.google.com/codelabs/android-workmanager/#0)
- [Android Paging Codelab](https://codelabs.developers.google.com/codelabs/android-paging/index.html?index=..%2F..%2Findex#0)
- [Android Lifecycle-aware components Codelab](https://codelabs.developers.google.com/codelabs/android-lifecycles/#0) (LiveData and ViewModel)
- [Android Persistence Codelab](https://codelabs.developers.google.com/codelabs/android-persistence/#0) (Room)
-