画面遷移時の型安全なデータ渡し
Safe Argsを利用すると、画面遷移時に遷移先フラグメントに対してデータを渡す方法も、型安全になります。次にその方法を紹介していきます。
ナビゲーショングラフへの引数登録
例えば、MemoDetailFragmentは、MemoListFragmentから遷移時に、タップされたリストデータの主キーidの値を受け取り、画面を表示するとします。通常のフラグメント間の遷移や、前回のリスト6では、Bundleオブジェクトであるargumentsを利用します。
一方、画面遷移時に型安全でのデータの受け渡しを行う場合、まず、ナビゲーショングラフの遷移先デスティネーションに受け取るデータを登録する必要があります。ナビゲーショングラフにおいてmemoDetailFragmentを選択すると、Attributesツールバーは、図1の表示となっています。
このAttributesのうちのArgumentsセクションの[+]をクリックすると、図2のAdd Argumentダイアログが表示されます。このダイアログが、まさにデスティネーションが受け取るデータを登録するためのものです。
先述のように、MemoDetailFragmentは主キーidの値を受け取る必要があるので、図2のように、name(データ名)をmemoId、type(データ型)をLongとし、さらに、データをうまく受け取れなかった場合のための初期値(Default Value)として0Lを入力し、[Add]をクリックします。
すると、Attributesツールバーは図3のように表示され、無事Argument(=引数)が登録されたのがわかります。
ここで、XML記述を見ておきましょう。これはリスト5のようになっています。
<?xml version="1.0" encoding="utf-8"?>
<navigation
:
app:startDestination="@id/memoListFragment">
:
<fragment
android:id="@+id/memoDetailFragment"
:
tools:layout="@layout/fragment_memo_detail" >
<argument
android:name="memoId"
app:argType="long"
android:defaultValue="0L" />
</fragment>
</navigation>
idがmemoDetailFragmentのfragmentタグ内にargumentタグが自動的に定義されています。そして、属性として定義されている値は、まさに、図2で入力した値そのものです。
遷移先にデータを渡すコード
このように遷移先画面に引数を定義すると、Javaコードの場合は、Action〇〇のインスタンスに、データを格納するためのデータ名のセッターが自動生成されています。そのため、リスト6の(1)と(2)の間に、(3)のコードを記述するだけでデータの格納が可能となります。ActionMemoListFragmentToMemoDetailFragmentの場合は、memoIdのセッター、すなわち、setMemoId()が自動生成されているので(1)で取得したactionに対して、(3)のようにsetMemoId()を実行してデータを格納します。
MemoListFragmentDirections.ActionMemoListFragmentToMemoDetailFragment action = MemoListFragmentDirections.actionMemoListFragmentToMemoDetailFragment(); // (1) action.setMemoId(memoId); // (3) navController.navigate(action); // (2)
Kotlinの場合は、このセッターがプロパティアクセスとなるため、リスト7の(3)のコードとなります。
val action = MemoListFragmentDirections.actionMemoListFragmentToMemoDetailFragment() // (1) action.memoId = memoId // (3) navController.navigate(action) // (2)
遷移先でデータを受け取るJavaコード
このようにして格納されたデータを、遷移先デスティネーションで受け取るコードは、Javaではリスト8のようになります。
MemoDetailFragmentArgs memoDetailFragmentArgs = MemoDetailFragmentArgs.fromBundle(getArguments()); // (1) long memoId = memoDetailFragmentArgs.getMemoId(); // (2)
リスト8のようにargumentタグが設定されたデスティネーションでは、そのデスティネーションクラス名の末尾にArgsをつけたクラスが自動生成されています。MemoDetailFragmentならば、MemoDetailFragmentArgsです。そして、このクラスの中に遷移元から渡されたデータが格納されることになっています。
そこで、まず、このMemoDetailFragmentArgsオブジェクトを取得します。それが、リスト8の(1)です。〇〇Argsクラスには、staticメソッドとしてfromBundle()が自動生成されているので、このメソッドを利用します。その際、引数として、Bundleオブジェクトであるarguments、すなわち、getArguments()の戻り値を渡します。
こうして取得した〇〇Argsオブジェクトには、argumentタグで設定したデータのゲッターが自動生成されているので、このゲッターを利用してデータを取得します。リスト8の(2)が、そのコードです。
遷移先でデータを受け取るKotlinコード
一方、Kotlinコードは、Javaコードとは違うものとなり、リスト9のコードとなります。
private val _memoDetailFragmentArgs: MemoDetailFragmentArgs by navArgs() // (1)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_memoDetailViewModel.memoId = _memoDetailFragmentArgs.memoId // (2)
}
Kotlinの場合は、〇〇Argsオブジェクトは、フラグメントクラスのプロパティとして定義します。それが、リスト9の(1)です。そして、navArgs()関数による委譲プロパティとして、インスタンスの用意を任せます。
その後、任意のメソッド内で、〇〇Argsオブジェクトのプロパティを利用してデータを取得します。それが(2)です。リスト9では、onCreate()メソッド内で遷移元からのデータを取得し、ViewModelに格納するコード例としていますが、もちろん、他のメソッド内でも利用できます。
