AndroidFragment监听返回键的一种合理方式

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

AndroidFragment监听返回键的⼀种合理⽅式
开场
以下场景为杜撰:
产品经理:“⼩罗,这个信息发送界⾯,如果⽤户输⼊了内容,点击返回键的时候,要先询问⽤户是否保存草稿箱哈”。

⼩罗:“收到,这问题简单。


说完⼩罗就准备着⼿处理,然后却发现信息编辑界⾯是⼀个Fragment,然⽽Fragment并没有提供返回键点击的直接处理;⼩罗虽菜,但是摸鱼也摸了些年头了,这问题难不倒⼩罗。

⼩罗⼼想,反正Activity提供了onBackPressed⽅法,再不济的情况把这个操作分发到Fragment中去就好,可是对于处⼥座的⼩罗来说,在解决问题的基础上,起码代码要写的漂亮⼀点,写的漂亮⼀点⼼⾥就舒服⼀点,⼼⾥舒服⼀点就...(此处内容很长)。

⼩罗坚信“条条⼤路通罗马”,我们不仅要到罗马,还要风风光光的去,所以对于“Fragment如何监听返回键的点击”,⼩罗决定下点功夫;
为什么关注的点是Fragment去监听返回键,⽽不是其他?其实在现在的开发过程中,Fragment的使⽤⽐重是⾮常
⼤的,对于个⼈⽽⾔,⼏乎整个⼯程的界⾯实现都是基于Fragment⽽⾮Activity。

⼀、最lowB的⽅式(不推荐)
这就是⼩罗⼼⾥的预备⽅案,在实在没有办法的时候会采⽤此⽅法,也就是前⾯提到的,我们可以在Activity执⾏onBackPressed时,分发到Fragment中去;那我们⽤什么来分发呢?这个分发就好⽐是连接Activity和Fragment之间的⼀个纽带,双⽅均能够访问到这个对象就可以了,所以⼀个可以的选择之⼀是使⽤ViewModel,当然还可以有其他选择,在此就不细聊了。

⼆、使⽤OnKeyListener(不推荐)
这种⽅式可能不常⽤,不容易想到这⽅⾯,所以这种⽅式也不推荐,简单做个了解;
通过设置View的OnKeyListener来监听返回键的处理,此⽅法也没什么⼤的弊端,只是要注意以下两点:
1、如果把这个功能封装在Fragment基类中的话,可能存在被覆盖的问题;⽐如在基类中设置了OnKeyListener,⽽⼦类也需要设置OnKeyListener,此时设置的监听则会替换默认设置的监听,从⽽导致意想不到的可能,不过此问题⼏乎不太可能发⽣。

2、需要注意这种⽅式将会改变返回键处理的顺序,也就是会先处理OnKeyListener的回调,再处理Activity的onBackPressed,所以要注意这个关系。

三、Jetpack提供的⽅式
其实对于返回键的分发,官⽅已经做了⽀持,在Activity中提供了⼀个⽤于分发返回键事件的对象,通过调⽤Activity的getOnBackPressedDispatcher()⽅法得到这个对象,由于这个对象是在⽐较底层的ponentActivity中提供的(AppCompatActivity->FragmentAcitivty->ponentActivity),所以在Fragment中可以直接拿到这个对象添加回调;
//官⽅使⽤⽰例
public class FormEntryFragment extends Fragment {
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
//定义回调
OnBackPressedCallback callback = new OnBackPressedCallback(
true // default to enabled
) {
@Override
public void handleOnBackPressed() {
showAreYouSureDialog();
}
};
//获取Activity的返回键分发器添加回调
requireActivity().getOnBackPressedDispatcher().addCallback(
this, // LifecycleOwner
callback);
}
}
简单明了,这个事情好像到此为⽌了~~
但随着深⼊了解,事情似乎没有这么简单,经过源码分析和资料收集,发现如果直接使⽤会存在以下弊端:
1、Fragment回调处理时,⽆法向上传递
2、回调是否可⽤需要主动标记,⽽⾮运⾏时确定
简单说⼀下OnBackPressedDispatcher分发返回键的流程:
//官⽅源码
@MainThread
public void onBackPressed() {
Iterator<OnBackPressedCallback> iterator =
mOnBackPressedCallbacks.descendingIterator();
while (iterator.hasNext()) {
OnBackPressedCallback callback = iterator.next();
if (callback.isEnabled()) {
callback.handleOnBackPressed();
return;
}
}
if (mFallbackOnBackPressed != null) {
mFallbackOnBackPressed.run();
}
}
当分发返回键事件时,会倒序循环遍历已经注册的回调,如果回调isEnabled设置为true,则执⾏回调的⽅法,分发结束;
那前⾯提到的弊端是怎么产⽣的呢?假如⼀个Activity有两个Fragment A和B,均注册了返回键点击事件(有童鞋会说了,这种场景不太可能存在,确实,这种场景是不多,但不代表没有,做⼀些了解也不是坏事),并且两个回调的isEnabled均设置为true,那么当分发事件时,会将事件分发给B,但是B此时并不需要处理返回键事件,但是B⼜没有办法再继续将事件传递给A了;
“你傻啊,你B不执⾏返回键事件,就设置isEnable为false啊”
“是啊,B不执⾏事件是该设置为false,可是我怎么知道什么时候去把它设置成false?难道动态绑定判断条件的值进⾏设置么?”
转头⼀想“咦,好像确实可以动态修改回调的isEnabled值呢,将回调的值跟⼀个LiveData绑定不就可以了么!”
理是这个理,但是我不愿意做额外的⼯作,我不愿这么⼲,谁知道动态判断条件到底有多复杂呢,难道我不可以在返回键点击的时候去判断么?
四、灵机⼀动,官⽅升级版(推荐⽅式)
官⽅的⽅式不是存在上⾯两个弊端么,解决这两个问题不就好了;所以结合官⽅OnBackPressedDispatcher和OnKeyListener 两者的优点,创建了andme.arch.activity.AMBackPressedDispatcher,在保留官⽅原有的功能的同时,更改事件分发流程,并将返回键持有者⼀并传⼊,⽤于解决⼀些更复杂⼀点的需求;
@MainThread
fun onBackPressed(): Boolean {
if (!hasRegisteredCallbacks())
return false
val iterator = mOnBackPressedCallbacks.descendingIterator()
while (iterator.hasNext()) {
val callback = iterator.next()
//判断回调是否需要消耗事件在决定是否继续传递
if (callback.handleOnBackPressed(owner)) {
return true
}
}
return false
}
五、官⽅使⽤技巧版
这种⽅法其实是我在发布⽂章之后,群友提供的⼀种思路,说实话,⾮常有技巧,刚开始看到的时候眼前⼀亮;其核⼼原理是
默认注册的回调是可⽤的,在回调执⾏中,先判断⾃⼰是否需要执⾏回调,如果不需要执⾏回调,则将⾃⼰的isEnabled设置为false,然后再调⽤OnBackPressedDispatcher重新分发返回键事件(由于此时已将⾃⼰设置为false,此时便不会响应回调),调⽤⽅法之后再将isEnabled设置为true,巧⽤了递归,该⽅式不错的;
最开始群友提供的代码有⼀丢丢瑕疵,以下为修正之后的代码,在Fragment中定义这两个⽅法,在需要绑定返回键监听的时候调⽤这个两个⽅法之⼀即可(推荐调⽤与⽣命周期相关的⽅法);
fun addOnBackPressed(onBackPressed: () -> Boolean): OnBackPressedCallback {
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (!onBackPressed()) {
isEnabled = false
requireActivity().onBackPressedDispatcher.onBackPressed()
isEnabled = true
}
}
}
requireActivity().onBackPressedDispatcher.addCallback(callback)
return callback
}
fun addOnBackPressed(owner: LifecycleOwner, onBackPressed: () -> Boolean): OnBackPressedCallback {
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (!onBackPressed()) {
isEnabled = false
requireActivity().onBackPressedDispatcher.onBackPressed()
isEnabled = true
}
}
}
requireActivity().onBackPressedDispatcher.addCallback(owner,callback)
return callback
}
但是经过慎重思考,最终我还是没有⽤这种⽅法,虽然这种⽅法在⼏乎百分之⼋九⼗的情况下是没有问题的,但是我认为可能还是有场景⽆法满⾜;
举个例⼦,⼀个Activity添加了⼀个Fragment,这个Fragment⼜顺序添加了A和B两个ChildFragment,那在B执⾏返回处理的时候,是想回到A还是finish呢?或者是其他呢,也是就是说我们⽆法确定,在Fragment执⾏返回键处理时,是否需要直接调⽤Activity.super.onBackPressed⽅法的可能。

我们永远⽆法预估⽤户的场景到底有多复杂,需求有多变态,所以尽可能的考虑把。

总结
综上所述,我⽬前还是会继续使⽤第四种我写的⽅案,第五种⽅案也推荐,毕竟在绝⼤部分场景中都是没有问题的
那么我们考虑第四种⽅案到底是否可⾏?
1、功能性
满⾜了功能需求,并且⾄少⽬前是没有想到有任何可能出现问题的场景
2、侵⼊性
⼏乎对⽤户场景没什么影响吧,只是对⽤户提供了⼀个可见的处理返回键事件的⽅法⽽已
3、替换性
如果采⽤第四种⽅案,要更换成第五种⽅案,容易么?⼀两句代码的事情⽽已
或者更换成其他⽅案容易么?也是⼀两句代码的的事情⽽已
并且即便替换成其他⽅案,也不会对现有系统造成任何影响,因为对于Fragment监听返回键这个需求来讲,这个需求的核⼼就是需要⼀个在Fragment中处理返回键事件的⽅法⽽已,其他东西对⽤户来讲都是⽆感的
所以总体觉得没什么⽑病;
如果你有更好的思路,欢迎沟通,不胜感激;
另外,上述功能其实并不仅仅⽀持在Fragment中处理返回键事件,理论上来说任何想要监听返回键处理的都可以通过Activity 获取AMBackPressedDispatcher对象添加回调即可。

到此这篇关于Android Fragment监听返回键的⼀种合理⽅式的⽂章就介绍到这了,更多相关Android Fragment监听返回键内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。

相关文档
最新文档