热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

这篇文章主要给大家介绍了关于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()方法得到这个对象,由于这个对象是在比较底层的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 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监听返回键内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 深入解析 Android 中 EditText 的 getLayoutParams 方法及其代码应用实例 ... [详细]
  • 提升Android开发效率:Clean Code的最佳实践与应用
    在Android开发中,提高代码质量和开发效率是至关重要的。本文介绍了如何通过Clean Code的最佳实践来优化Android应用的开发流程。以SQLite数据库操作为例,详细探讨了如何编写高效、可维护的SQL查询语句,并将其结果封装为Java对象。通过遵循这些最佳实践,开发者可以显著提升代码的可读性和可维护性,从而加快开发速度并减少错误。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 观察 | 求职体验:收到录用通知的公司通常不深究技术细节,而那些详细追问的公司往往没有后续进展
    观察 | 求职体验:收到录用通知的公司通常不深究技术细节,而那些详细追问的公司往往没有后续进展 ... [详细]
  • 在开发过程中,为了确保代码的实时保存,我们常常会对某个功能进行多次提交。然而,当功能趋于稳定时,将这些分散的提交记录合并为一次提交,可以提高代码仓库的整洁度和可维护性。本文将详细介绍如何使用 Git 巧妙地合并多次提交记录,帮助开发者简化历史记录管理。 ... [详细]
  • 本文探讨了一种高效的算法,用于生成所有数字(0-9)的六位组合,允许重复使用数字,并确保这些组合的和等于给定的整数N。该算法通过优化搜索策略,显著提高了计算效率,适用于大规模数据处理和组合优化问题。 ... [详细]
  • SSL 错误:目标主机名与备用证书主题名称不匹配
    在使用 `git clone` 命令时,常见的 SSL 错误表现为:无法访问指定的 HTTPS 地址(如 `https://ip_or_domain/xxxx.git`),原因是目标主机名与备用证书主题名称不匹配。这通常是因为服务器的 SSL 证书配置不正确或客户端的证书验证设置有问题。建议检查服务器的 SSL 证书配置,确保其包含正确的主机名,并确认客户端的证书信任库已更新。此外,可以通过临时禁用 SSL 验证来排查问题,但请注意这会降低安全性。 ... [详细]
  • C++ 开发实战:实用技巧与经验分享
    C++ 开发实战:实用技巧与经验分享 ... [详细]
  • Presto:高效即席查询引擎的深度解析与应用
    本文深入解析了Presto这一高效的即席查询引擎,详细探讨了其架构设计及其优缺点。Presto通过内存到内存的数据处理方式,显著提升了查询性能,相比传统的MapReduce查询,不仅减少了数据传输的延迟,还提高了查询的准确性和效率。然而,Presto在大规模数据处理和容错机制方面仍存在一定的局限性。本文还介绍了Presto在实际应用中的多种场景,展示了其在大数据分析领域的强大潜力。 ... [详细]
  • 在开发过程中,我最初也依赖于功能全面但操作繁琐的集成开发环境(IDE),如Borland Delphi 和 Microsoft Visual Studio。然而,随着对高效开发的追求,我逐渐转向了更加轻量级和灵活的工具组合。通过 CLIfe,我构建了一个高度定制化的开发环境,不仅提高了代码编写效率,还简化了项目管理流程。这一配置结合了多种强大的命令行工具和插件,使我在日常开发中能够更加得心应手。 ... [详细]
  • 提升 Kubernetes 集群管理效率的七大专业工具
    Kubernetes 在云原生环境中的应用日益广泛,然而集群管理的复杂性也随之增加。为了提高管理效率,本文推荐了七款专业工具,这些工具不仅能够简化日常操作,还能提升系统的稳定性和安全性。从自动化部署到监控和故障排查,这些工具覆盖了集群管理的各个方面,帮助管理员更好地应对挑战。 ... [详细]
  • 在探讨 MySQL 正则表达式 REGEXP 的功能与应用之前,我们先通过一个小实验来对比 REGEXP 和 LIKE 的性能。通过具体的代码示例,我们将评估这两种查询方式的效率,以确定 REGEXP 是否值得深入研究。实验结果将为后续的详细解析提供基础。 ... [详细]
  • 本文介绍了 Vue 开发的入门指南,重点讲解了开发环境的配置与项目的基本搭建。推荐使用 WebStorm 作为 IDE,其下载地址为 。安装时请选择适合您操作系统的版本,并通过 获取激活码。WebStorm 是前端开发者的理想选择,提供了丰富的功能和强大的代码编辑能力。 ... [详细]
  • 每日前端实战:148# 视频教程展示纯 CSS 实现按钮两侧滑入装饰元素的悬停效果
    通过点击页面右侧的“预览”按钮,您可以直接在当前页面查看效果,或点击链接进入全屏预览模式。该视频教程展示了如何使用纯 CSS 实现按钮两侧滑入装饰元素的悬停效果。视频内容具有互动性,观众可以实时调整代码并观察变化。访问以下链接体验完整效果:https://codepen.io/comehope/pen/yRyOZr。 ... [详细]
  • 计算机视觉领域介绍 | 自然语言驱动的跨模态行人重识别前沿技术综述(上篇)
    本文介绍了计算机视觉领域的最新进展,特别是自然语言驱动的跨模态行人重识别技术。上篇内容详细探讨了该领域的基础理论、关键技术及当前的研究热点,为读者提供了全面的概述。 ... [详细]
author-avatar
大姑氵娘祖茉茉_749
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有