リンク先間でデータを渡す

Navigation を使用すると、リンク先の引数を定義してナビゲーション処理にデータをアタッチできます。たとえば、ユーザー プロフィールのリンク先でユーザー ID の引数を受け取って、表示するユーザーを決めることができます。

一般にリンク先間では、最小限の量のデータのみを渡すことを強くおすすめします。たとえば、オブジェクト自体を渡すのではなく、オブジェクトを取得するためのキーを渡す必要があります。これは、Android では、保存されたすべての状態の合計容量が限られているためです。大量のデータを渡す必要がある場合は、フラグメント間でデータを共有するで説明されているとおり、ViewModel を使用することを検討してください。

リンク先の引数を定義する

リンク先間でデータを渡すには、最初に引数を定義します。そのためには、次の手順に沿って、引数を受け取るリンク先に引数を追加します。

  1. Navigation Editor で、引数を受け取るリンク先をクリックします。
  2. [Attributes] パネルで [Add]([+])をクリックします。
  3. 表示された [Add Argument Link] ウィンドウで、引数名、引数の型、引数に null を指定できるかどうか、デフォルト値(必要な場合)を入力します。
  4. [Add] をクリックします。これで、[Attributes] パネルの [Arguments] リストに引数が表示されます。
  5. 次に、対応するアクションをクリックします(リンク先に移動します)。これで、[Attributes] パネルの [Argument Default Values] に、新たに追加した引数が表示されるはずです。
  6. また、引数が XML に追加されたことも確認できます。[Text] タブをクリックすると XML ビューに切り替わり、引数を受け取るリンク先に引数が追加されたことを確認できます。以下に例を示します。

     <fragment android:id="@+id/myFragment" >
             <argument
                 android:name="myArg"
                 app:argType="integer"
                 android:defaultValue="0" />
         </fragment>
        

サポートされている引数の型

Navigation ライブラリでは、以下の引数の型がサポートされています。

app:argType の構文 デフォルト値のサポート null 値のサポート
Integer app:argType="integer" ×
Float app:argType="float" ×
Long app:argType="long" ○ - デフォルト値は必ず接尾辞「L」で終わる必要があります(例: 「123L」)。 ×
Boolean app:argType="boolean" ○ - 「true」または「false」。 ×
String app:argType="string"
リソース参照 app:argType="reference" ○ - デフォルト値には「@resourceType/resourceName」の形式(例: 「@style/myCustomStyle」)または「0」を指定する必要があります。 ×
カスタム Parcelable app:argType="<type>"(<type> は Parcelable の完全修飾クラス名) デフォルト値「@null」をサポートしています。他のデフォルト値はサポートしていません。
カスタム Serializable app:argType="<type>"(<type> は Serializable の完全修飾クラス名) デフォルト値「@null」をサポートしています。他のデフォルト値はサポートしていません。
カスタム Enum app:argType="<type>"(<type> は Enum の完全修飾名) ○ - デフォルト値が非修飾名と一致する必要があります(例: 「SUCCESS」は MyEnum.SUCCESS と一致)。 ×

引数の型が null 値をサポートしている場合は、android:defaultValue="@null" を使用して null のデフォルト値を宣言できます。

カスタム型のいずれかを選択すると、[Select Class] ダイアログが表示され、その型に対応するクラスを選択するよう促されます。[Project] タブでは、現在のプロジェクトからクラスを選択できます。

<inferred type> を選択すると、指定した値に基づいて Navigation ライブラリに型を決めさせることができます。

選択した [Type] の値の配列を引数に指定する必要があることを示すには、[Array] チェックボックスをオンにします。なお、Enum の配列とリソース参照の配列はサポートされていません。基となる型の null 可能性に関係なく、配列には常に null を使用できます。配列は、単一のデフォルト値「@null」をサポートしています。他のデフォルト値はサポートしていません。

アクションでリンク先の引数をオーバーライドする

リンク先レベルの引数とデフォルト値は、リンク先に移動するアクションのすべてで使用されます。必要に応じて、アクション レベルで引数を定義することにより、引数のデフォルト値をオーバーライドできます(デフォルト値をまだ設定していない場合は設定できます)。この引数は、リンク先で宣言されている引数と名前および型が同じである必要があります。

次の XML では、上の例のリンク先レベルの引数をオーバーライドする引数を使用してアクションを宣言しています。

<action android:id="@+id/startMyFragment"
        app:destination="@+id/myFragment">
        <argument
            android:name="myArg"
            app:argType="integer"
            android:defaultValue="1" />
    </action>
    

Safe Args を使用して、型の安全性が保証されたデータを渡す

Navigation Architecture Component には、Safe Args と呼ばれる Gradle プラグインが含まれています。Safe Args は、リンク先とアクション用に指定された引数にタイプセーフにアクセスするためのシンプルなオブジェクトとビルダークラスを生成します。Safe Args を使用すると型の安全性が保証されるため、ナビゲーションを使用する場合にデータを渡す方法としておすすめです。

場合によっては、Safe Args プラグインを使用できないこともあります(Gradle を使用していない場合など)。そのような場合は、Bundle を使用してデータを直接渡すことができます。

Safe Args を有効にすると、生成したコードに、各アクション用のタイプセーフ メソッドと、送信側と受信側のリンク先が追加されます。これらのクラスについて以下で説明します。

  • アクションの元のリンク先ごとにクラスが作成されます。このクラスの名前は、元のリンク先の名前に「Directions」というワードが付加されたものです。たとえば、元のリンク先が SpecifyAmountFragment という名前のフラグメントの場合、SpecifyAmountFragmentDirections という名前のクラスが作成されます。

    このクラスには、元のリンク先で定義された各アクション用のメソッドが含まれます。

  • 引数を渡すために使用されるアクションごとに、アクションに基づく名前を持つ内部クラスが作成されます。たとえば、confirmationAction, という名前のアクションの場合、クラスの名前は ConfirmationAction になります。defaultValue が指定されていない引数がアクションに含まれている場合は、関連付けられているアクション クラスを使用して引数の値を設定します。

  • 受信側のリンク先用のクラスが作成されます。このクラスの名前は、リンク先の名前に「Args」というワードが付加されたものです。たとえば、リンク先のフラグメントが ConfirmationFragment, という名前の場合、ConfirmationFragmentArgs という名前のクラスが作成されます。このクラスの fromBundle() メソッドを使用して引数を取得します。

次の例では、これらのメソッドを使用して引数を設定し、navigate() メソッドに渡す方法を示しています。

Kotlin

    override fun onClick(v: View) {
       val amountTv: EditText = view!!.findViewById(R.id.editTextAmount)
       val amount = amountTv.text.toString().toInt()
       val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
       v.findNavController().navigate(action)
    }
    

Java

    @Override
    public void onClick(View view) {
       EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount);
       int amount = Integer.parseInt(amountTv.getText().toString());
       ConfirmationAction action =
               SpecifyAmountFragmentDirections.confirmationAction()
       action.setAmount(amount)
       Navigation.findNavController(view).navigate(action);
    }
    

受信側のリンク先のコードで、getArguments() メソッドを使用してバンドルを取得し、その内容を使用します。-ktx 依存関係を使用する場合、Kotlin ユーザーは by navArgs() プロパティのデリゲートを使用して引数にアクセスすることもできます。

Kotlin

    val args: ConfirmationFragmentArgs by navArgs()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val tv: TextView = view.findViewById(R.id.textViewAmount)
        val amount = args.amount
        tv.text = amount.toString()
    }
    

Java

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        TextView tv = view.findViewById(R.id.textViewAmount);
        int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
        tv.setText(amount + "")
    }
    

Safe Args とグローバル アクションを併用する

Safe Args とグローバル アクションを併用する場合、次の例に示すように、ルートの <navigation> 要素で android:id の値を指定する必要があります。

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:app="http://schemas.android.com/apk/res-auto"
                xmlns:tools="http://schemas.android.com/tools"
                xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/main_nav"
                app:startDestination="@id/mainFragment">

        ...

    </navigation>
    

Navigation は、android:id の値に基づく <navigation> 要素用に Directions クラスを生成します。たとえば、<navigation> 要素で android:id=@+id/main_nav が指定されている場合、MainNavDirections という名前のクラスが生成されます。<navigation> 要素内のすべてのリンク先では、前のセクションで説明したのと同じメソッドを使用して、関連付けられているすべてのグローバル アクションにアクセスするためのメソッドが生成されています。

Bundle オブジェクトを使用してリンク先間でデータを渡す

Gradle を使用していない場合でも、Bundle オブジェクトを使用することで、リンク先間で引数を渡すことができます。以下に示すように、Bundle オブジェクトを作成し、navigate() を使用してリンク先に渡します。

Kotlin

    var bundle = bundleOf("amount" to amount)
    view.findNavController().navigate(R.id.confirmationAction, bundle)
    

Java

    Bundle bundle = new Bundle();
    bundle.putString("amount", amount);
    Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
    

受信側のリンク先のコードで、getArguments() メソッドを使用して Bundle を取得し、その内容を使用します。

Kotlin

    val tv = view.findViewById<TextView>(R.id.textViewAmount)
    tv.text = arguments.getString("amount")
    

Java

    TextView tv = view.findViewById(R.id.textViewAmount);
    tv.setText(getArguments().getString("amount"));
    

最初のリンク先にデータを渡す

アプリの最初のリンク先にデータを渡すことができます。まず、データを保持する Bundle を明示的に作成する必要があります。次に、次のいずれかのメソッドを使用して、Bundle を最初のリンク先に渡します。

最初のリンク先でデータを取得するには、Fragment.getArguments() を呼び出します。

ProGuard に関する考慮事項

コードを圧縮する場合は、軽量化プロセスの一環として、ParcelableSerializableEnum の各クラス名が難読化されないようにする必要があります。これは、次の 2 つのうちのいずれかの方法で実現できます。

@Keep アノテーションを使用する

次の例では、@Keep アノテーションをモデルクラスの定義に追加しています。

Kotlin

    @Keep class ParcelableArg : Parcelable { ... }

    @Keep class SerializableArg : Serializable { ... }

    @Keep enum class EnumArg { ... }
    

Java

    @Keep public class ParcelableArg implements Parcelable { ... }

    @Keep public class SerializableArg implements Serializable { ... }

    @Keep public enum EnumArg { ... }
    

keepnames ルールを使用する

次の例に示すように、keepnames ルールを proguard-rules.pro ファイルに追加することもできます。

proguard-rules.pro

...

    -keepnames class com.path.to.your.ParcelableArg
    -keepnames class com.path.to.your.SerializableArg
    -keepnames class com.path.to.your.EnumArg

    ...
    

参考情報

ナビゲーションについて詳しくは、以下の参考情報をご確認ください。

サンプル

コードラボ

動画