［Android］Navigation でシンプルな画面遷移を最速で実装する

# はじめに

`Jetpack` の `Navigation` はすごく便利ですよね。
最近ちょくちょく利用するのですが実装の仕方を忘れてしまいます。

なので忘れても `Navigation` を最速で実装できるようにするため、
1つの `Activity` で 複数の`Fragment` を切り替えるアプリの作成方法をまとめます。

![Dec-04-2019 09-02-30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/9bc31ec2-ba7f-f074-a63a-be9af9d82c8c.gif)

# 準備

`Navigation` を利用したアプリケーションを作成するための下準備をします。
次の手順で必要なライブラリやクラス、レイアウトを生成していきます。

## `Navigation` をセットアップする

次の内容を `dependencies` に記述すると`Navigation`をインストールできます。 
`Java` と `Kotlin` でインストール方法が違うので注意が必要です。

```groovy:build.gradle(app)
dependencies {
    def nav_version = "2.1.0"
    implementation "androidx.navigation:navigation-fragment:$nav_version"
    implementation "androidx.navigation:navigation-ui:$nav_version"

    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}
```

## `Fragment` を作成する

`Navigation` を使った遷移で利用する `Fragment` を作成していきます。
次の手順で `OneFragment`, `TwoFragment`, `ThreeFragment` の 3つを作成します。

1. `Package Name` を右クリックし、`New → Fragment → Fragment(Blank)` を選択する
2. `Fragment Name` に `任意の名称` を入力し `OK` を押す
3. `Fragment` にいらない記述が書かれているので消す
4. `Layout` に `Fragment` に遷移させるための `Button` を追加する


```Fragment.kt
class OneFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_one, container, false)
    }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/17d9d3ca-5359-1d11-6a58-652dc5751eb1.png)


```layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".OneFragment">

    <Button
            android:id="@+id/nav_button"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="@string/hello_blank_fragment" />

</FrameLayout>
```

# 実装
 
`Navigation` を利用するには次の3つを実装する必要があります。
それらの 3 つの実装を 1 つずつ進めていきます。

| No | 名称 | 役割 |
| -------- | ------ | ------ |
| 1 | `Navigation Graph`の作成 | 宛先などナビゲーションの情報を管理する XML ファイルを作成する |
| 2 | `Nav Host`の作成 | `Nav Host`は`Navigation Graph` の宛先を表示するための空のコンテナのこと<br> `Fragment` を表示するので`NavHost`の実装である`NavHostFragment`を作成する  |
| 3 | `Nav Controller`で画面を遷移させる| `Nav Host` に表示するコンテンツを`Navigation Graph`を元に<br>切り替える処理をする`Nav Controller`を取得し、画面を遷移させる。 |


## Navigation Graph の作成

宛先などのナビゲーション情報を管理する`Navigation Graph`を作成します。

### リソースファイルの作成

`Navigation Graph`はどこからどこへ遷移するかをリソースファイルに記述したものです。
なのでまずは`Navigation Graph`のリソースファイルを作成します。

1. `res` を右クリックし `New → Android Resource File` を選択する
2. `File name` に `nav_graph` , `Resource Type` に `Navigation` を入力する


### 遷移先の組み立て

作成した `nav_graph` を開くと`Navigation Editor`が表示されます。
この `Navigation Editor` を利用して、どこからどこへ遷移するかを決めていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/713c8ca3-552f-3571-50a4-4edb06f0aeb3.png)

次の操作でどこからどこへ遷移するか設定できます。今回は`OneFragment` から `TwoFragment`、
`TwoFragment`から`ThreeFragment``ThreeFragment`から`OneFragment`に遷移させます。

1. 📱をクリックして `OneFragment`, `TwoFragment`, `ThreeFragment` を追加する
2. `OneFragment`から`TwoFragment`, `TwoFramgent`から`ThreeFragment`、<br>`ThreeFragment`から`OneFragment` に線を引く


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/b41d0594-bd6b-32ea-efdd-e446d7ecdbac.png)

## `Nav Host` の作成

`Navigation Graph` で定義したコンテンツを表示するための空のコンテナを作成します。
`Activity`のレイアウトに`fragment`を追加した後、
次の`Attribute`を追加することで`Nav Host Fragment`を定義できます。


| Attribute | Value |  説明 |
| ------- | ------  | ------- |
| android:name | NavHostFragment | どの`NavHost`の実装を利用するか指定できる。<br>今回は`Fragment`を表示するための`NavHostFragment`を指定する。 |
| app:navGraph | @navigation/nav_graph | どの`Navigation Graph`を利用するか設定できる。<br>今回はさっき作成した`Navigation Graph`をここで指定する。 |
| app:defaultNavHost | true | `NavHostFragment`が`BACK`ボタンを無効化できる。<br>今回は`BACK`ボタンでホームに戻りたくないので有効化しておく | 

```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
            android:id="@+id/nav_host_fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:name="androidx.navigation.fragment.NavHostFragment"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph" />
</FrameLayout>
```

# `Nav Controller` で画面を遷移させる

`Nav Controller`を取得し、`Button`をクリックした時に`Fragment`を遷移させる処理を実装する。

## `Nav Controller` を取得する

`Nav Controller` を取得する方法は 3つあります。
次のように `Fragment` か `View` , `Activity` から取得する方法があります。

```
Fragment.findNavController()
View.findNavController()
Activity.findNavController(viewId: Int)
```

## `Nav Controller` で遷移する

`Nav Controller` を取得したら `Navigate` で画面を遷移できます。
この`Navigate`の引数には`ID`指定し、どこからどこへ遷移するか指定します。
この`ID`ですが`Nav Graph`を設定する`Navigation Editor`で矢印をクリックすると確認できます。

```
NavController.navigate(ID)
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/c34a6e18-e110-c4f4-46bc-1c41fc2d491b.png)


説明した`Nav Controller`を取得する方法と`Nav Controller`で遷移する方法を組み合わせて
次のように`Fragment`にコードを実装すれば画面遷移できるようになります。

```Fragment.kt
class OneFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_one, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Fragment を使った Nav Controller の取得
        val navController = this.findNavController()
        val button = view.findViewById<Button>(R.id.nav_button)
        button.setOnClickListener {
            navController.navigate(R.id.action_oneFragment_to_twoFragment)
        }

        // View を使った Nav Controller の取得
        val button = view.findViewById<Button>(R.id.nav_button)
        button.setOnClickListener { view ->
            val navController = view.findNavController()
            navController.navigate(R.id.action_oneFragment_to_twoFragment)
        }
    }
}
```

# 動作確認

実装が完了したらアプリケーションをインストールして動作確認します。
ボタンをクリックすると次の`Fragment`に遷移できてますね。

![Dec-04-2019 09-02-30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/e1f0af8a-8fa6-6c16-a2c7-4add13607b66.gif)

# おわりに

`Navigation`は複雑そうに見えますが簡単です。
これからは`Navigation`で簡単に早く画面遷移を実装しましょう。

# 参考文献

- [NavigationSample | GitHub](https://github.com/kaleidot725/android-mylab/tree/master/NavigationSample)
- [ナビゲーション | Android Developers](https://developer.android.com/guide/navigation?gclid=Cj0KCQiAz53vBRCpARIsAPPsz8XQqaWN6AxZ0_twqaq-BgvXFUnXAqMPeLcEbFBFKYg-Yc_KEdbYSIQaAolBEALw_wcB)
- [Get started with the Navigation component](https://developer.android.com/guide/navigation/navigation-getting-started)