Passing data between Fragment
s can be achieved in various ways, including using the target Fragment
APIs (Fragment.setTargetFragment()
and Fragment.getTargetFragment()
), ViewModel
or the Fragment
s’ parent Activity
. The target Fragment
APIs have recently been deprecated, and the encouraged way to pass data between Fragment
s is now the Fragment
result APIs, where passing the data is handled by a FragmentManager
, and Fragment
s can be set up to receive and send data.
在Fragment
之间传递数据的方式有多种,包括使用目标Fragment
API( Fragment.setTargetFragment()
和Fragment.getTargetFragment()
), ViewModel
或Fragment
的父Activity
。 目标Fragment
API最近已被弃用,现在鼓励使用的在Fragment
之间传递数据的方式是Fragment
结果API,其中通过FragmentManager
处理数据的传递,并且Fragment
可以设置为接收和发送数据。
在片段之间传递数据 (Passing Data Between Fragments)
To pass data between 2 Fragment
s without them having references to each other, you can now use their common FragmentManager
, which acts as a central store for data passed between Fragment
s.
要在两个Fragment
之间传递数据而又彼此之间没有引用,您现在可以使用它们的公用FragmentManager
,它充当Fragment
之间传递的数据的中央存储。
接收资料 (Receiving Data)
If a Fragment
expects to receive data, it can register a FragmentResultListener
on a FragmentManager
and specify a key to identify the data it expects, this acts as a filter to the data the FragmentManager
sends it.
如果一个Fragment
预期接收数据时,它可以注册一个FragmentResultListener
上的FragmentManager
和指定一个密钥,以确定它期望数据,这作为一个过滤器的数据FragmentManager
发送。
FragmentManager.setFragmentResultListener(
requestKey,
lifecycleOwner,
FragmentResultListener { requestKey: String, result: Bundle ->
// Handle result
})
The lifecycleOwner
argument allows for data to be received only if it’s at least in a STARTED
state. If the LifecycleOwner
is the Fragment
itself, you can safely update the UI when new data is received, since the View
will have been created by that point (onViewCreated()
is called before onStart()
).
lifecycleOwner
参数仅在数据至少处于STARTED
状态时才允许接收数据。 如果LifecycleOwner
是Fragment
本身,你可以当接收新的数据安全地更新UI,因为View
将是由点创建( onViewCreated()
被调用之前onStart()
If multiple data is passed to the Fragment
before it reaches the STARTED
state, it’ll only receive the latest value if it eventually reaches the STARTED
state.
如果有多个数据传递给Fragment
到达之前STARTED
状态,它只会收到最新的值,如果它最终到达STARTED
状态。
When the LifecycleOwner
reaches the DESTROYED
state, it automatically unregisters the listener. To manually unregister it beforehand, call FragmentManager.setFragmentResultListener()
again with the same request key, only this time pass in a null
FragmentResultListener
.
当LifecycleOwner
达到DESTROYED
状态时,它将自动注销注册侦听器。 要预先手动注销,请使用相同的请求密钥再次调用FragmentManager.setFragmentResultListener()
,仅这次传入null
FragmentResultListener
。
The FragmentManager
used to register the listener depends on the Fragment
s that can send back data.
该FragmentManager
用于注册监听器依赖于Fragment
s表示可以发送回数据。
If
FragmentA
expects to receive data fromFragmentB
and both are on the same level, they can both communicate through their parentFragmentManager
, andFragmentA
must register its listener using its parentFragmentManager
.如果
FragmentA
希望从FragmentB
接收数据并且两者都处于同一级别,则它们都可以通过其父FragmentManager
通信,并且FragmentA
必须使用其父FragmentManager
注册其侦听器。
parentFragmentManager.setFragmentResultListener(...)
If
FragmentA
expects to receive data fromFragmentB
andFragmentA
isFragmentB
’s parent, they can both communicate throughFragmentA
’s childFragmentManager
, which isFragmentB
’s parentFragmentManager
.如果
FragmentA
希望从FragmentB
接收数据,并且FragmentA
是FragmentB
的父级,则它们都可以通过FragmentA
的子FragmentManager
通信,后者是FragmentB
的父级FragmentManager
。
childFragmentManager.setFragmentResultListener(...)
Essentially, the listener must be set on a common FragmentManager
.
本质上,侦听器必须在公共 FragmentManager
上设置。
传送资料 (Sending Data)
If FragmentB
needs to send data to FragmentA
, it can pass it through a common FragmentManager
(the parent FragmentManager
) using the request key FragmentA
registered its listener with.
如果FragmentB
需要将数据发送到FragmentA
,它可以使用注册了其侦听器的请求键FragmentA
将其通过通用FragmentManager
(父FragmentManager
)传递。
parentFragmentManager.setFragmentResult(
requestKey, // Same request key FragmentA used to register its listener
bundleOf(key to value) // The data to be passed to FragmentA
)
测试片段结果 (Testing Fragment Results)
To test that a Fragment
receives or sends data successfully, you can use the FragmentScenario
API, which provides the benefit of testing your Fragment
in isolation.
要测试Fragment
是否成功接收或发送数据,可以使用FragmentScenario
API,它提供了隔离测试Fragment
的好处。
接收资料 (Receiving Data)
If FragmentA
registers a FragmentResultListener
to receive data, you can simulate the action of sending this data using the Fragment
’s parent FragmentManager
. If FragmentA
’s listener is set up correctly, it should receive the data, verify this by either retrieving it from FragmentA
, or verifying a side effect of the reception, for example, if FragmentA
displays data it receives on the UI, verify the expected data is displayed using Espresso
APIs.
如果FragmentA
注册一个FragmentResultListener
来接收数据,则可以使用Fragment
的父FragmentManager
来模拟发送此数据的操作。 如果FragmentA
的侦听器设置正确,它应该接收数据,或者通过从FragmentA
检索它来验证数据,或者验证接收的副作用,例如,如果FragmentA
在UI上显示它接收到的数据,请验证预期的数据使用Espresso
API显示。
@Test
fun shouldReceiveData() {
val scenario = FragmentScenario.launchInContainer(FragmentA::class.java)
// Pass data using the parent fragment manager
scenario.onFragment { fragment ->
val data = bundleOf(KEY_DATA to "value")
fragment.parentFragmentManager.setFragmentResult("aKey", data)
}
// Verify data is received, for example, by verifying it's been displayed on the UI
onView(withId(R.id.textView)).check(matches(withText("value")))
}
传送资料 (Sending Data)
You can test whether FragmentB
correctly sends data by setting up a FragmentResultListener
on its parent FragmentManager
inside the test, sending the result, then verifying the listener successfully received the data.
您可以测试是否FragmentB
正确通过设置数据发送FragmentResultListener
其父FragmentManager
测试中,将结果发送,然后验证成功接收数据的监听器。
@Test
fun shouldSendData() {
val scenario = FragmentScenario.launchInContainer(FragmentB::class.java)
// Register result listener
var receivedData = ""
scenario.onFragment { fragment ->
fragment.parentFragmentManager.setFragmentResultListener(
KEY,
fragment,
FragmentResultListener { key, result ->
receivedData = result.getString(KEY_DATA)
})
}
// Send data
onView(withId(R.id.send_data)).perform(click())
// Verify data was successfully sent
assertThat(receivedData).isEqualTo("value")
}
样例项目 (Sample Project)
Below is a sample project that demonstrates using the Fragment
Result APIs.
下面是一个示例项目,演示了如何使用Fragment
Result API。
结论 (Conclusion)
While the deprecation of the Fragment
target APIs and their replacement with the Fragment
result APIs is a bit limiting since the latter only allows for Bundle
s to be passed, thus only simple type, Serializable
and Parcelable
data can be passed, the Fragment
result APIs allow to properly recover from process death and to pass data between Fragment
s without the need to hold references to each other, which prevents issues that may occur from accessing a Fragment
in an unpredictable state.
尽管Fragment
目标API的弃用和用Fragment
结果API的替换是一个位限制,因为后者仅允许传递Bundle
,因此只能传递简单类型,可Serializable
和可Parcelable
数据,但Fragment
结果API允许可以从进程终止中正确恢复并在Fragment
之间传递数据,而无需彼此之间保持引用,这可以防止在无法预测的状态下访问Fragment
可能发生的问题。
Want more Fragment
s goodness? Check out:
是否需要更多Fragment
的优点? 退房:
翻译自: https://proandroiddev.com/android-fragments-fragment-result-805a6b2522ea