Fragment无法像Activity那样重写OnBackPressed方法,所以拦截Back键相对比较复杂,但这又是一个常见需求,例如以下场景:
在SearchFragment中,点击back键时,关闭SearchView而非直接退出
使用FragmentManger
一个常见做法是定义一个带有OnBackPressed的方法的接口或基类给Fragment继承:
interface BackPressHandler {
fun onBackPressed(): Boolean
}
然后,Fragment实现onBackPressed
class SearchFragment : Fragment(), BackPressHandler{
...
override fun onBackPressed(): Boolean {
if (searchBar.focusedChild == null) {
updateFocus(FocusSearchAction.BACK_PRESSED)
return true
}
return false
}
...
}
最后,在Activity中通过FragmentManager获取所有栈顶Fragment,然后分发Back事件
//MainActivity.kt
override fun onBackPressed() {
if (!isTopFragmentConsumedBackPress()) {
super.onBackPressed()
}
}
private fun isTopFragmentConsumedBackPress() = getTopFragment<BackPressHandler>()?.onBackPressed() == true
getTopFragment的实现如下:
inline fun <reified T> FragmentActivity.getTopFragment(): T?
= supportFragmentManager.fragments.firstOrNull()?.let { it as? T }
使用OnBackPressedDispatcher
如果你的项目正在使用androdx,androidx.activity
给我们提供了另一个实现方案:
ComponentActivity(FragmentActivity 和 AppCompatActivity的基类)提供了OnBackPressedDispatcher
用来设置callback,然后在handleOnBackPressed
中拦截处理back事件
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// in here you can do logic when backPress is clicked
}
})
}
OnBackPressedCallback可以设置isEnable,当isEnable为true时才会进行事件拦截。
如下,我们只是在特定条件下做一次处理,之后不再处理,此时可以在else中设置isEnable为false:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if(shouldInterceptBackPress()){
// in here you can do logic when backPress is clicked
}else{
isEnabled = false
activity?.onBackPressed()
}
}
})
}
使用OnBackPressedDispatcher无需再定义额外接口和基类。但是它最大的有优点是可以感知生命周期
LifecycleAware
看一下addCallback的定义就知道:
public void addCallback(@NonNull LifecycleOwner owner,
@NonNull OnBackPressedCallback onBackPressedCallback) {
Lifecycle lifecycle = owner.getLifecycle();
if (lifecycle.getCurrentState() == Lifecycle.State.DESTROYED) {
return;
}
onBackPressedCallback.addCancellable(
new LifecycleOnBackPressedCancellable(lifecycle, onBackPressedCallback));
}
onBackPressedCallback会被封装成LifecycleOnBackPressedCancellable
,他接受一个LifecycleOwner,只有当Lifecycle.State.STARTED时候,callback才会被真正注册,反之当destroyed的时候,会被自动注销,避免泄露
Chain of Responsibility
所有的Fragment,无论嵌套层级有多深,都基于同一个Activity添加callback,这要求back事件的消费具有顺序性:
依次添加了三个callback A,B,C,则事件被消费的顺序是C,B,A,即逆序消费。
当Framgent嵌套时,能保证叶子节点的Fragment最先消费
这种设计模式是常见的责任链模式。
需要注意调用 addCallback()
时,callback并非立即注册到责任链,只有当LifecycleOwner 为Lifecycle.State.STARTED 时才会添加,这在前面已经讲过。