从 Fragment 1.3.0-alpha04 开始,每个 FragmentManager 都会实现 FragmentResultOwner。这意味着 FragmentManager 可以充当 Fragment 结果的集中存储区。此更改通过设置 Fragment 结果并监听这些结果,而不要求 Fragment 直接引用彼此,让单独的 Fragment 相互通信。
如需将数据从 Fragment B 传回到 Fragment A,请先在接收结果的 Fragment A 上设置结果监听器。在 Fragment A 的 FragmentManager 上调用 setFragmentResultListener() 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...
}
});
}
图 1. Fragment B 使用 FragmentManager 将数据发送到 Fragment A。
在生成结果的 Fragment 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);
}
});
然后,在值为 STARTED 后,Fragment A 便会收到结果并执行监听器回调。
对于一个指定键,您只能有一个监听器和一个结果。如果您对同一个键多次调用 setResult(),则系统会将 Fragment B 从返回堆栈退出之前的最近结果发送给 Fragment A。如果您设置的结果没有相应的监听器来接收,则结果会存储在 FragmentManager 中,直到您设置一个具有相同键的监听器。请注意,监听器的 Fragment 必须为 STARTED,然后该 Fragment 才能收到结果。监听器收到结果并触发 onFragmentResult() 回调后,结果会被清除。这种行为有两个主要影响:
返回堆栈上的 Fragment 只有在被弹出栈顶且为 STARTED 之后才会收到结果。
如果在设置结果时监听结果的 Fragment 为 STARTED,则系统会立即触发监听器的回调。
注意:某个 Fragment 为 DESTROYED 后,您就无法再对该 Fragment 设置监听器。
在父级 Fragment 和子级 Fragment 之间传递结果
如需将结果从子级 Fragment 传递到父级 Fragment,父级 Fragment 在调用 setFragmentResultListener() 时应使用 getChildFragmentManager() 而不是 getParentFragmentManager()。
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. 子级 Fragment 可以使用 FragmentManager 将结果发送到其父级 Fragment。
子级 Fragment 在其 FragmentManager 上设置结果。然后,当父级 Fragment 为 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);
}
});
测试 Fragment 结果
使用 FragmentScenario 测试对 setFragmentResult() 和 setFragmentResultListener() 的调用。使用 launchFragmentInContainer 或 launchFragment 为被测 Fragment 创建场景,然后手动调用目前未测试的方法。
如需测试 setResultListener(),请使用调用 setResultListener() 的 Fragment 创建一个场景。接下来,直接调用 setResult() 并验证结果:
@Test
fun testFragmentResultListener() {
val scenario = launchFragmentInContainer()
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() 的 Fragment 创建一个场景。接下来,直接调用 setResultListener() 并验证结果:
@Test
fun testFragmentResult() {
val scenario = launchFragmentInContainer()
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))
}
}
}