开场

以下场景为杜撰:

产品经理:“小罗,这个信息发送界面,如果用户输入了内容,点击返回键的时候,要先询问用户是否保存草稿箱哈”。

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

说完小罗就准备着手处理,然后却发现信息编辑界面是一个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()方法得到这个对象,由于这个对象是在比较底层的androidx.activity.ComponentActivity中提供的(AppCompatActivity->FragmentAcitivty->androidx.activity.ComponentActivity),所以在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对象添加回调即可。

Andme Github地址

到此这篇关于Android Fragment监听返回键的一种合理方式的文章就介绍到这了,更多相关Android Fragment监听返回键内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Android Fragment监听返回键的一种合理方式的更多相关文章

  1. android – 尝试commitAllowingStateLoss时的FragmentManager NullPointerException

    上下文:我有一个带片段的活动和3个InnerFragments.当调用FragmentonDestroy()时,我想从FragmentManager中删除内部片段.onDestroy()的代码如下.问题:FragmentManager抛出NullPointerException,可能是在调用commitAllowingStateLoss()时.我不明白为什么.堆栈跟踪:解决方法FragmentMa

  2. android – Slider Menu片段中的可交换选项卡

    我已经通过引用thistutorial实现了导航抽屉,现在我想要做的是在片段内显示滑动标签.即当我点击导航抽屉中的一个项目时,让我们说第一个项目,它应显示该项目的滑动标签.如果item1是Events,当我点击它时,它应该显示滑动标签.但我面临以下问题:–>如何在片段内实现视图寻呼机?

  3. android – setRetainInstance(true)setCustomAnimations(…)=每个方向更改的动画?

    >为什么我仍然可以获得添加片段的动画?>其他配置发生变化时会发生什么?

  4. android – 来自Fragment的getActionBar和AppCompatLibrary

    我正在寻找使用AppCompatLibrary和API8从Fragment获取ActionBar实例的最简单方法.已经尝试过像但没有运气.解决方法试着施展它:

  5. android – onActivityCreated总是被调用?

    有人可以提供一些链接来做解释生命周期行为的文档吗?>究竟什么是Fragmentrestart()?>Android可以决定删除不可见的片段但是保留包含它们的活动吗?注1:我测试过,由于活动重新创建而添加了Fragment时调用了onActivityCreated,并且在活动完全启动并激活后手动添加片段时也是如此.注2:我正在使用23.3.0支持版本进行测试.某些行为是否有可能从以前的版本发生变化?我相信Android操作系统杀死唯一可管理的量子是一个过程.对于Fragment,您可以查看FragmentA

  6. android – Fragment,保存onSaveInstanceState上的大型数据列表(如何防止TransactionTooLargeException)

    如何存储(和恢复)我的Fragment状态.在ViewPager中的片段上使用setRetainInstance是否可以?解决方法为了保留大块数据,Google建议使用保留实例的Fragment.想法是创建没有视图的空片段和所有必需的字段,否则这些字段将被保存在Bundle中.添加setRetainInstance;到Fragment的onCreate方法.而不是将数据保存在Activity的onDestroy上的Fragment中并将它们加载到onCreate上.以下是活动的例子:片段的例子:

  7. Android在FragmentPagerAdapter中的Fragment中设置TextView文本

    更新:那么做这样的事情?

  8. android – AsyncTask,Fragments,Views和Backstacks

    我一直在使用这种模式.这是一个非常人为的AsyncTask进度指示器示例:方向更改它可以正常工作,但我注意到当我从backstack中弹出片段时,片段非空,fragment.getView()为null.这显然会导致崩溃.你们用什么方法?我似乎无法在网上找到完美的解决方案.重要提示,这是一个片段,我调用setRetainInstance;在onActivityCreated(…

  9. 带有动画的Android Fragment Transaction会导致白色闪烁

    我有两个片段.片段A最初在视图中.当用户按下按钮时,使用以下方法将片段B动画到视图中.当我弹出片段B时,它会在视图中向下移动,但在完成时屏幕会闪烁白色.不确定是什么导致这种情况,似乎只发生在kit-kat而不是棒棒糖上.正在使用的动画是向上滑动并向下滑动在xml中定义的动画.解决方法对我来说,这个崩溃动画的bottomBarNavigation,NavigationDrawer以及当我使用替换片段

  10. android – 如何使用兼容包显示DialogFragment?

    我是否必须使用另一种方式来显示带有兼容性库的MyDialogFragment?任何帮助都感激不尽.版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部