[Android] Navigationの基礎の基礎

# はじめに
今回はAndroid JetPackのNavigationについてまとめました。
「これからNavigationに移行しようと思ってるけどどうやれば...」
「Navigationってそもそもなんぞや」
という方向けの記事になります。

# Navigaiton とは
アプリの画面遷移をサポートするライブラリです。
特徴として、
- アプリの画面遷移を手軽に実装できる
- 視覚的に画面遷移の動線を確認できる
- GUIから設定が可能
があり、かなり直感的に画面遷移の設定をすることができます。

# サンプルアプリ
Navigationの基礎機能を確認するためにサンプルアプリを取得しましょう。
今回は[architecture-conponents-sample](https://github.com/android/architecture-components-samples)の中にある[NavigationBasicSample](https://github.com/android/architecture-components-samples/tree/main/NavigationBasicSample)を使用します。
なおこのリポジトリはAndroid-Architecture-ComponentsというモダンなAndoridアプリ開発を手助けしてくれるライブラリ群全体のサンプルコードがありますので、ぜひ他のコードも見てみてください。

# Navigation Graphの設定
サンプルコードのnavigation.xmlがNavigation Graphになります。
ここには画面遷移させたいActivity, Fragmentを記述することで、コードからNavigationを設定することができるようになります。
```
<!-- app:startDestinationで指定したFragmentが自動的にbackstackの一番下（最初に起動するFragment）になる -->
<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/title_screen">
　　　　　　　　<!-- fragmentタグを使ってFragmentを指定 -->
    <!-- activityタグもあるので、別Activityに飛びたい時はactivityタグを指定する -->
    <fragment
            android:id="@+id/title_screen"
            android:name="com.example.android.navigationsample.TitleScreen"
            android:label="fragment_title_screen"
            tools:layout="@layout/fragment_title_screen">
        <action
                android:id="@+id/action_title_screen_to_register"
                app:destination="@id/register"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"/>
        <action
                android:id="@+id/action_title_screen_to_leaderboard"
                app:destination="@id/leaderboard"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"/>
    </fragment>
    <!-- 略 -->
</navigation>
```

他タグの属性に関しては下でさらに説明させていただきます。

# actionを使った画面遷移
fragmentタグの下にactionタグを設定することで、対象のFragmentからの画面遷移を設定することができます。
```
<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/title_screen">

    <fragment
            android:id="@+id/title_screen"
            android:name="com.example.android.navigationsample.TitleScreen"
            android:label="fragment_title_screen"
            tools:layout="@layout/fragment_title_screen">
        <!-- ここで設定 -->
        <action
　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　<!-- 固有のIDを指定 -->
                android:id="@+id/action_title_screen_to_register"
                <!-- 遷移先のIDをここで指定 -->
                app:destination="@id/register"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"/>
        <!-- 略 -->
    </fragment>
    <fragment
            android:id="@+id/register"
            android:name="com.example.android.navigationsample.Register"
            android:label="fragment_register"
            tools:layout="@layout/fragment_register">
        <action
                android:id="@+id/action_register_to_match"
                app:destination="@id/match"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"/>
    </fragment>
    <!-- 略 -->
</navigation>
```
Nav Graphでactionの設定ができたらこれを遷移元のFragmentから呼び出します
```
// 基本的な呼び方
Navigation.findNavController(view).navigate(R.id.action_title_screen_to_register)
// ktxを使うと短くなる
findNavController().navigate(R.id.action_title_screen_to_register)
```
たったこれだけで画面遷移の実装をすることができます。
しかもBackStackも自動で積まれているので、遷移先からバックキーで戻ると遷移元に戻ることができます。

# Transitions
actionタグから遷移時のアニメーションを設定することができます。
```
<action
    android:id="@+id/action_title_screen_to_register"
    app:destination="@id/register"
    <!-- ここで設定 -->
    <!-- 戻るアクションにより表示されるときのアニメーション -->
    app:popEnterAnim="@anim/slide_in_left"
    <!-- 戻るアクションにより非表示になるときのアニメーション -->
    app:popExitAnim="@anim/slide_out_right"
    <!-- 画面遷移により表示されるときのアニメーション -->
    app:enterAnim="@anim/slide_in_right"
    <!-- 画面遷移により非表示になるときのアニメーション -->
    app:exitAnim="@anim/slide_out_left"/>
```
サンプルアプリでは横にスライドしていくようなアニメーションになっていますが、独自アニメーションを設定することで画面遷移の体験を変えることもできます。

# 遷移時の引数
Safe Argsを使用することで、遷移元から遷移先に値を渡すことができます。
```
<fragment
    android:id="@+id/user_profile"
    android:name="com.example.android.navigationsample.UserProfile"
    android:label="fragment_user_profile"
    tools:layout="@layout/fragment_user_profile">
    <!-- ここで設定 -->
　　　　　　　　<!-- 複数設定したい場合にはargumentタグを並べていくと設定可能 -->
    <argument android:name="userName"
        <!-- argTypeで型の設定が可能　-->
　　　　　　　　　　　　　　　　　<！-- defaultValueの設定も可能 -->
        android:defaultValue="name"/>
</fragment>
```
設定ができたらFragmentから呼び出します。
```
holder.item.setOnClickListener {
    val bundle = bundleOf("userName" to myDataset[position])

    Navigation.findNavController(holder.item).navigate(
        R.id.action_leaderboard_to_userProfile,
        bundle
    )
}
```

# DeepLinkの指定
DeepLinkの設定をすることで、指定したLinkからアプリを起動すると対象のFragmentを直で起動させることができます。
```
<fragment
    android:id="@+id/user_profile"
    android:name="com.example.android.navigationsample.UserProfile"
    android:label="fragment_user_profile"
    tools:layout="@layout/fragment_user_profile">
    <argument android:name="userName"
        android:defaultValue="name"/>
    <!-- ここで設定 -->
    <!-- argumentがある場合にはそれをDeepLinkに指定することで遷移ができるようになる -->
    <!-- 逆に指定しないと引数なしでクラッシュするので注意 -->
    <deepLink app:uri="www.example.com/user/{userName}" />
</fragment>
```

# 終わりに
今回はNavigationについてまとめてみました。
記事の中ではNav Graphに直接記載する形で設定をしましたが、AndoridStudio 3.3以上からはGUIからの設定も可能になっており、なお一層直感的に設定ができるようになっています。

# 参考資料
サンプルコード
https://github.com/android/architecture-components-samples/tree/main/NavigationBasicSample
Navigationの公式リファレンス
https://developer.android.com/jetpack/androidx/releases/navigation?hl=ja