フラグメント間でデータを渡す

Fragment 1.3.0-alpha04 以降では、各 FragmentManagerFragmentResultOwner を実装します。つまり、FragmentManager はフラグメントの結果の中心的なストアとして機能できます。この変更により、フラグメントの結果を設定してリッスンすることで、個々のフラグメントが互いに通信できるようになります。フラグメントは互いに直接参照する必要がなくなります。

フラグメント B からフラグメント A にデータを返すには、まず、フラグメント A(結果を受け取るフラグメント)で結果リスナーを設定します。次の例に示すように、フラグメント A の FragmentManagersetFragmentResultListener() API を呼び出します。

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Use the Kotlin extension in the fragment-ktx artifact
    setResultListener("requestKey") { key, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported
        val result = bundle.getString("bundleKey")
        // Do something with the result...
    }
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getParentFragmentManager().setFragmentResultListener("key", this, new FragmentResultListener() {
        @Override
        public void onFragmentResult(@NonNull String key, @NonNull Bundle bundle) {
            // We use a String here, but any type that can be put in a Bundle is supported
            String result = bundle.getString("bundleKey");
            // Do something with the result...
        }
    });
}
フラグメント B はフラグメント マネージャを使用してフラグメント A にデータを送信する
図 1. フラグメント B は FragmentManager を使用してフラグメント A にデータを送信する。

フラグメント B(結果を生成するフラグメント)では、同じ requestKey を使用して同じ FragmentManager に結果を設定する必要があります。これを行うには、setFragmentResult() API を使用します。

Kotlin

button.setOnClickListener {
    val result = "result"
    // Use the Kotlin extension in the fragment-ktx artifact
    setResult("requestKey", bundleOf("bundleKey" to result))
}

Java

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Bundle result = new Bundle();
        result.putString("bundleKey", "result");
        getParentFragmentManager().setFragmentResult("requestKey", result);
    }
});

フラグメント A は結果を受け取り、STARTED になるとリスナー コールバックを実行します。

特定のキーに対してリスナーと結果を 1 つだけ設定できます。同じキーに対して setResult() を複数回呼び出した場合は、フラグメント B がバックスタックからポップされる前の最新の結果がフラグメント A に送信されます。結果を受け取る対応するリスナーを指定せずに結果を設定した場合は、同じキーでリスナーを設定するまで、結果は FragmentManager に格納されます。リスナーのフラグメントが結果を受け取るには、その前にフラグメントが STARTED になる必要がある点に注意してください。リスナーが結果を受け取り、onFragmentResult() コールバックを送信すると、結果はクリアされます。この動作には主に 2 つ意味があります。

  • バックスタックのフラグメントは、ポップされて STARTED になるまでは結果を受け取りません。
  • 結果が設定されたときに結果をリッスンしているフラグメントが STARTED である場合、リスナーのコールバックはすぐに送信されます。

親フラグメントと子フラグメントの間で結果を渡す

子フラグメントから親に結果を渡すには、親フラグメントは setFragmentResultListener() を呼び出すときに getParentFragmentManager() ではなく getChildFragmentManager() を使用する必要があります。

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // We set the listener on the child fragmentManager
    childFragmentManager.setResultListener("requestKey") { key, bundle ->
        val result = bundle.getString("bundleKey")
        // Do something with the result..
    }
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // We set the listener on the child fragmentManager
    getChildFragmentManager().setFragmentResultListener("key", this, new FragmentResultListener() {
        @Override
        public void onFragmentResult(@NonNull String key, @NonNull Bundle bundle) {
            String result = bundle.getString("bundleKey");
            // Do something with the result..
        }
    });
}
子フラグメントはフラグメント マネージャを使用して親に結果を送信できる
図 2. 子フラグメントは FragmentManager を使用して親に結果を送信できる。

子フラグメントは FragmentManager で結果を設定します。フラグメントが STARTED になると、親は結果を受け取ります。

Kotlin

button.setOnClickListener {
    val result = "result"
    // Use the Kotlin extension in the fragment-ktx artifact
    setResult("requestKey", bundleOf("bundleKey" to result))
}

Java

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Bundle result = new Bundle();
        result.putString("bundleKey", "result");

        // The child fragment needs to still set the result on its parent fragment manager
        getParentFragmentManager().setFragmentResult("requestKey", result);
    }
});

フラグメントの結果をテストする

FragmentScenario を使用して、setFragmentResult()setFragmentResultListener() の呼び出しをテストします。テスト用のフラグメントのシナリオを作成するには、launchFragmentInContainer または launchFragment を使用し、テストされていないメソッドを手動で呼び出します。

setResultListener() をテストするには、setResultListener() を呼び出すフラグメントを使ってシナリオを作成します。次に、setResult() を直接呼び出して、結果を確認します。

@Test
fun testFragmentResultListener() {
    val scenario = launchFragmentInContainer<ResultListenerFragment>()
    scenario.onFragment { fragment ->
        val expectedResult = "result"
        fragment.parentFragmentManagager.setResult("requestKey", bundleOf("bundleKey" to expectedResult))
        assertThat(fragment.result).isEqualTo(expectedResult)
    }
}

class ResultListenerFragment : Fragment() {
    var result : String? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Use the Kotlin extension in the fragment-ktx artifact
        setResultListener("requestKey") { key, bundle ->
            result = bundle.getString("bundleKey")
        }
    }
}

setResult() をテストするには、setResult() を呼び出すフラグメントを使ってシナリオを作成します。次に、setResultListener() を直接呼び出して、結果を確認します。

@Test
fun testFragmentResult() {
    val scenario = launchFragmentInContainer<ResultFragment>()
    lateinit var actualResult: String?
    scenario.onFragment { fragment ->
        fragment.parentFragmentManagager.setResultListener("requestKey") { key, bundle ->
            actualResult = bundle.getString("bundleKey")
        }
    }
    onView(withId(R.id.result_button)).perform(click())
    assertThat(actualResult).isEqualTo("result")
}

class ResultFragment : Fragment(R.layout.fragment_result) {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        view.findViewById(R.id.result_button).setOnClickListener {
            val result = "result"
            // Use the Kotlin extension in the fragment-ktx artifact
            setResult("requestKey", bundleOf("bundleKey" to result))
        }
    }
}