【Android】Navigationでデータ受け渡しを伴う画面遷移をする方法

# はじめに

皆さん、ごきげんよう！れぶです！

今回の記事では、Android Jetpack系の[Navigation](https://developer.android.com/guide/navigation?hl=ja)を使って、データの受け渡しをしつつ、Fragment間の画面を遷移させる手順を書いていきます。

自身が実装をする上で[公式ドキュメント](https://developer.android.com/guide/navigation/navigation-pass-data?hl=ja)を参照したのですが、個人的に少し理解に苦しんだ箇所がありました。なので、注意すべきことも含めて詳細にまとめていこうと思います。

それでは、参りましょう！！

# この記事の対象者

* Navigationで、画面間のデータ受け渡しを伴う遷移を実現したい方
* Navigationを使った画面遷移の処理は分かるけど、画面間のデータ受け渡しの処理に苦戦している方
* 画面間のデータ受け渡しの処理の手順を知りたい方

ただし、[公式ドキュメント](https://developer.android.com/guide/navigation/navigation-pass-data?hl=ja#Safe-args)でもある通り、大量のデータを渡す必要がある場合、`ViewModel`の使用が推奨されているので、そこは注意してください！

# 開発環境
* Mac
* Android Studio Bumblebee
* Java 8
* compileSdkVersion 31
* minSdkVersion 16
* Navigation 2.4.1

# 動作イメージ

最終的な動作イメージは以下になります。
<img width="300" alt="ezgif.com-gif-maker.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456250/c89733c5-72d8-c8ab-87a8-0579994eba9f.gif">

⑴　最初の画面（InputFragment）で、整数を入力
⑵　「判断」ボタンを押す
⑶　次の画面(OutputFragment)で、⑴で入力した値が7の倍数の場合には「YES」、そうでない場合は「NO」が表示される

至ってシンプルな作りです。

# 実装手順

それでは、本題に入ります。以下の4ステップで処理を実現します。

## ①build.gradleファイルに追加

Navigationで型安全的にデータを受け渡すために、『`Safe Args`』というGradleプラグインを使用します。そのためにまず、project・appレベル両方でbuild.gradleにそれぞれ以下を追加し『`Safe Args`』を有効にします。

執筆時点でのNavigationのバージョンは2.4.1ですが、適宜変更してください。

```build.gradle(project)
dependencies {
    def nav_version = "2.4.1"
    classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
```

```build.gradle(app)
plugins {
    id 'androidx.navigation.safeargs'
}
```
**ただし、上記二つを同期しエラーが出る際には、Android StudioやGradle、Javaのバージョンが足りていない可能性があるので、疑ってください。**（エラー内容にもよりますが）

自身はAndroid StudioやGradle、Javaのバージョン全てが足りておらず、時間を浪費してしまったので、注意ポイントです。

## ②遷移先に変数を設定

Navigation Editor（Designタブ）でナビゲーショングラフに設定を加えていきます。大まかな手順は以下です。

⑴データを受け取る**遷移先**を選択する（※）
⑵「Attributes」パネルの「Arguments」リストの「＋」（赤枠）を選択する
⑶「Add Argument」ダイアログで、nameとtype等を入力する
⑷「Add」を選択する

※ 下記の画像のように、inputFragmentからoutputFragmentに遷移させる際にデータも一緒に受け渡すので、今回は`outputFragment`が**遷移先**になります。
![スクリーンショット 2022-02-23 16.09.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456250/df43bddf-7886-8791-17d5-f0ff5e43beb7.png)
ここでは、遷移先にどんなデータを渡したいのかを設定しています。
以下は該当箇所のXMLコードです。

```nav_graph.xml
<fragment
        android:id="@+id/outputFragment"
        android:name="com.myapp.OutputFragment"
        android:label="fragment_output"
        tools:layout="@layout/fragment_output" >
        <argument
            android:name="result"
            app:argType="string" />
    </fragment>
```
今回の例ではinputFragmentから、変数名が「result」、データ型が「String」のデータ（「YES」か「NO」）をoutputFragmentに渡すように設定しました。

## ③遷移元にデータの送信処理を記述

```InputFragment.java
String result = 「YES」か「NO」;

InputFragmentDirections.ActionInputFragmentToOutputFragment action = 
        InputFragmentDirections.actionInputFragmentToOutputFragment(result);

Navigation.findNavController(v).navigate(action);
```

コードの中身を解説します。
まずはアクションを作成していきます。

「Safe Args」プラグインを有効にすると、以下の二つのルールで遷移元にアクション向けのクラスやメソッドが自動生成されます。なので、**自分で新規でクラスやメソッドを作る必要は全くありません。**

⑴
|  | 遷移元のFragment名 | 生成されるクラス |
|:-:|:-:|:-:|
|ルール  |「◯◯◯Fragment」   |「◯◯◯FragmentDirections」   |
|今回の例   |「InputFragment」   |「InputFragmentDirections」   |

⑵
|  |actionタグのid属性 | 生成されるクラス |生成されるメソッド（※）|
|:-:|:-:|:-:|:-:|
|ルール  |「action_◯◯◯」   |「Action◯◯◯」   |「action◯◯◯」|
|今回の例   |「action_inputFragment<br>_to_outputFragment」   |「ActionInputFragment<br>ToOutputFragment」   |「actionInputFragment<br>ToOutputFragment」|

※このメソッドの引数には受け渡したいデータを入れます。今回は、7の倍数によって「YES」か「NO」を入れてあげます。

つまり、**遷移元のFragment名やactionタグのid属性によって、クラスやメソッドが生成される**ということです。

そうして作ったアクションを`NavController`の`navigate`メソッドの引数に設置してあげれば、データ送信を伴う遷移処理はOKです。

## ④遷移先にデータの受信処理を記述

```OutputFragment.java
String result = OutputFragmentArgs.fromBundle(getArguments()).getResult();
```
こちらもコードの中身を解説します。
上記③のステップで送信されたデータを受け取っていきましょう。

「Safe Args」プラグインを有効にするとで、以下のルールで遷移先にクラスが自動生成されます。なので、**自分で新規でクラスを作る必要は全くありません。**

|  | 遷移先のFragment名 | 生成されるクラス |
|:-:|:-:|:-:|
|ルール  |「◯◯◯Fragment」   |「◯◯◯FragmentArgs」   |
|今回の例   |「OutputFragment」  |「OutputFragmentArgs」  |

その「OutputFragmentArgs」クラスの`fromBundle`メソッドの引数には、`getArguments`メソッドを設定します。そして、それを`getResult`メソッドで取得すれば、データの受け渡しを伴う遷移の処理全てが完了します。

`getResult`メソッドは、**上記②のステップで遷移先に設定した変数名によって変わる**ので、気を付けてください。

# サンプルコード

とここで、自身が書いたコードを[GitHub](https://github.com/seiyaleb/NavigationTrialApp)に載っけているので、参考の一つにしてみてください。

# おわりに
今回は、`Navigation`でデータの受け渡しを伴う画面遷移の仕方を紹介させて頂きました。4ステップで実現できるので、意外と簡単だと感じた方も少なくないと思います。

* Android StudioやGradle等のバージョンに気をつける
* 遷移先のArgumentsに変数を設定する
* 遷移元・先の名前に沿ったクラスやメソッドが自動生成される

この辺りを注意して実装していくのが良いのかなと感じました。
以上です。ありがとうございました！

# 参考サイト
* [デスティネーション間でデータを渡す](https://developer.android.com/guide/navigation/navigation-pass-data?hl=ja)「公式ドキュメント」
* [Navigation コンポーネント スタートガイド](https://developer.android.com/guide/navigation/navigation-getting-started?hl=ja)「公式ドキュメント」
* [Navigation Componentの使い方（概要〜画面遷移〜データの受け渡し編）](https://qiita.com/naoi/items/8384561d30111c8704b3#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89)
* [Navigationコンポーネントの使い方編【Android Jetpack】](https://toronavi.com/jetpack-navigation#toc15)