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

Android如何优雅的处理重复点击

这篇文章主要介绍了Android如何优雅的处理重复点击,帮助大家更好的理解和学习使用Android开发,感兴趣的朋友可以了解下

一般手机上的 Android App,主要的交互方式是点击。用户在点击后,App 可能做出在页面内更新 UI、新开一个页面或者发起网络请求等操作。Android 系统本身没有对重复点击做处理,如果用户在短时间内多次点击,则可能出现新开多个页面或者重复发起网络请求等问题。因此,需要对重复点击有影响的地方,增加处理重复点击的代码。

之前的处理方式

之前在项目中使用的是 RxJava 的方案,利用第三方库 RxBinding 实现了防止重复点击:

fun View.onSingleClick(interval: LOng= 1000L, listener: (View) -> Unit) {
 RxView.clicks(this)
  .throttleFirst(interval, TimeUnit.MILLISECONDS)
  .subscribe({
   listener.invoke(this)
  }, {
   LogUtil.printStackTrace(it)
  })
}

但是这样有一个问题,比如使用两个手指同时点击两个不同的按钮,按钮的功能都是新开页面,那么有可能会新开两个页面。因为 Rxjava 这种方式是针对单个控件实现防止重复点击,不是多个控件。

现在的处理方式

现在使用的是时间判断,在时间范围内只响应一次点击,通过将上次单击时间保存到 Activity Window 中的 decorView 里,实现一个 Activity 中所有的 View 共用一个上次单击时间。

fun View.onSingleClick(
 interval: Int = SingleClickUtil.singleClickInterval,
 isShareSingleClick: Boolean = true,
 listener: (View) -> Unit
) {
 setOnClickListener {
  val target = if (isShareSingleClick) getActivity(this)?.window?.decorView ?: this else this
  val millis = target.getTag(R.id.single_click_tag_last_single_click_millis) as? Long ?: 0
  if (SystemClock.uptimeMillis() - millis >= interval) {
   target.setTag(
    R.id.single_click_tag_last_single_click_millis, SystemClock.uptimeMillis()
   )
   listener.invoke(this)
  }
 }
}

private fun getActivity(view: View): Activity? {
 var cOntext= view.context
 while (context is ContextWrapper) {
  if (context is Activity) {
   return context
  }
  cOntext= context.baseContext
 }
 return null
}

参数 isShareSingleClick 的默认值为 true,表示该控件和同一个 Activity 中其他控件共用一个上次单击时间,也可以手动改成 false,表示该控件自己独享一个上次单击时间。

mBinding.btn1.onSingleClick {
 // 处理单次点击
}

mBinding.btn2.onSingleClick(interval = 2000, isShareSingleClick = false) {
 // 处理单次点击
}

其他场景处理重复点击

间接设置点击

除了直接在 View 上设置的点击监听外,其他间接设置点击的地方也存在需要处理重复点击的场景,比如说富文本和列表。

为此将判断是否触发单次点击的代码抽离出来,单独作为一个方法:

fun View.onSingleClick(
 interval: Int = SingleClickUtil.singleClickInterval,
 isShareSingleClick: Boolean = true,
 listener: (View) -> Unit
) {
 setOnClickListener { determineTriggerSingleClick(interval, isShareSingleClick, listener) }
}

fun View.determineTriggerSingleClick(
 interval: Int = SingleClickUtil.singleClickInterval,
 isShareSingleClick: Boolean = true,
 listener: (View) -> Unit
) {
 ...
}

直接在点击监听回调中调用 determineTriggerSingleClick 判断是否触发单次点击。下面拿富文本和列表举例。

富文本

继承 ClickableSpan,在 onClick 回调中判断是否触发单次点击:

inline fun SpannableStringBuilder.onSingleClick(
 listener: (View) -> Unit,
 isShareSingleClick: Boolean = true,
 ...
): SpannableStringBuilder = inSpans(
 object : ClickableSpan() {
  override fun onClick(widget: View) {
   widget.determineTriggerSingleClick(interval, isShareSingleClick, listener)
  }
  ...
 },
 builderAction = builderAction
)

这样会有一个问题, onClick 回调中的 widget,就是设置富文本的控件,也就是说如果富文本存在多个单次点击的地方, 就算 isShareSingleClick 值为 false,这些单次点击还是会共用设置富文本控件的上次单击时间。

因此,这里需要特殊处理,在 isShareSingleClick 为 false 的时候,创建一个假的 View 来触发单击事件,这样富文本中多个单次点击 isShareSingleClick 为 false 的地方都有一个自己的假的 View 来独享上次单击时间。

class SingleClickableSpan(
 ...
) : ClickableSpan() {

 private var mFakeView: View? = null

 override fun onClick(widget: View) {
  if (isShareSingleClick) {
   widget
  } else {
   if (mFakeView == null) {
    mFakeView = View(widget.context)
   }
   mFakeView!!
  }.determineTriggerSingleClick(interval, isShareSingleClick, listener)
 }
 ...
}

在设置富文本的地方,使用设置 onSingleClick 实现单次点击:

mBinding.tvText.movementMethod = LinkMovementMethod.getInstance()
mBinding.tvText.highlightColor = Color.TRANSPARENT
mBinding.tvText.text = buildSpannedString {
 append("normalText")
 onSingleClick({
  // 处理单次点击
 }) {
  color(Color.GREEN) { append("clickText") }
 }
}

列表

列表使用 RecyclerView 控件,适配器使用第三方库 BaseRecyclerViewAdapterHelper。

Item 点击:

adapter.setOnItemClickListener { _, view, _ ->
 view.determineTriggerSingleClick {
  // 处理单次点击
 }
}

Item Child 点击:

adapter.addChildClickViewIds(R.id.btn1, R.id.btn2)
adapter.setOnItemChildClickListener { _, view, _ ->
 when (view.id) {
  R.id.btn1 -> {
   // 处理普通点击
  }
  R.id.btn2 -> view.determineTriggerSingleClick {
   // 处理单次点击
  }
 }
}

数据绑定

使用 DataBinding 的时候,有时会在布局文件中直接设置点击事件,于是在 View.onSingleClick 上增加 @BindingAdapte 注解,实现在布局文件中设置单次点击事件,并对代码做出调整,这个时候需要将项目中 listener: (View) -> Unit 替换成 listener: View.OnClickListener。

@BindingAdapter(
 *["singleClickInterval", "isShareSingleClick", "onSingleClick"],
 requireAll = false
)
fun View.onSingleClick(
 interval: Int? = SingleClickUtil.singleClickInterval,
 isShareSingleClick: Boolean? = true,
 listener: View.OnClickListener? = null
) {
 if (listener == null) {
  return
 }

 setOnClickListener {
  determineTriggerSingleClick(
   interval ?: SingleClickUtil.singleClickInterval, isShareSingleClick ?: true, listener
  )
 }
}

在布局文件中设置单次点击:


在代码中处理单次点击:

class YourViewModel : ViewModel() {

 fun handleClick() {
  // 处理单次点击
 }
}

总结

对于直接在 View 上设置点击的地方,如果需要处理重复点击使用 onSingleClick,不需要处理重复点击则使用原来的 setOnClickListener。

对于间接设置点击的地方,如果需要处理重复点击,则使用 determineTriggerSingleClick 判断是否触发单次点击。

项目地址

single-click,觉得用起来很爽的,请不要吝啬你的 Star !

以上就是Android如何优雅的处理重复点击的详细内容,更多关于Android 处理重复点击的资料请关注其它相关文章!


推荐阅读
  • 优化局域网SSH连接延迟问题的解决方案
    本文介绍了解决局域网内SSH连接到服务器时出现长时间等待问题的方法。通过调整配置和优化网络设置,可以显著缩短SSH连接的时间。 ... [详细]
  • CentOS 7.2 配置防火墙端口开放
    本文介绍如何在 CentOS 7.2 系统上配置防火墙以开放特定的服务端口,包括 FTP 服务的临时与永久开放方法,以及如何验证配置是否生效。 ... [详细]
  • 本文探讨了如何通过最小生成树(MST)来计算严格次小生成树。在处理过程中,需特别注意所有边权重相等的情况,以避免错误。我们首先构建最小生成树,然后枚举每条非树边,检查其是否能形成更优的次小生成树。 ... [详细]
  • 本文介绍如何在现有网络中部署基于Linux系统的透明防火墙(网桥模式),以实现灵活的时间段控制、流量限制等功能。通过详细的步骤和配置说明,确保内部网络的安全性和稳定性。 ... [详细]
  • Git管理工具SourceTree安装与使用指南
    本文详细介绍了Git管理工具SourceTree的安装、配置及团队协作方案,旨在帮助开发者更高效地进行版本控制和项目管理。 ... [详细]
  • 本文详细介绍如何在Linux系统中配置SSH密钥对,以实现从一台主机到另一台主机的无密码登录。内容涵盖密钥对生成、公钥分发及权限设置等关键步骤。 ... [详细]
  • 对象自省自省在计算机编程领域里,是指在运行时判断一个对象的类型和能力。dir能够返回一个列表,列举了一个对象所拥有的属性和方法。my_list[ ... [详细]
  • Python 工具推荐 | PyHubWeekly 第二十一期:提升命令行体验的五大工具
    本期 PyHubWeekly 为大家精选了 GitHub 上五个优秀的 Python 工具,涵盖金融数据可视化、终端美化、国际化支持、图像增强和远程 Shell 环境配置。欢迎关注并参与项目。 ... [详细]
  • 并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性
    Java并发编程实践目录并发编程01——ThreadLocal并发编程02——ConcurrentHashMap并发编程03——阻塞队列和生产者-消费者模式并发编程04——闭锁Co ... [详细]
  • 本文将详细介绍如何在没有显示器的情况下,使用Raspberry Pi Imager为树莓派4B安装操作系统,并进行基本配置,包括设置SSH、WiFi连接以及更新软件源。 ... [详细]
  • 本文探讨了如何通过一系列技术手段提升Spring Boot项目的并发处理能力,解决生产环境中因慢请求导致的系统性能下降问题。 ... [详细]
  • 本文详细介绍如何通过设置SSH密钥来获取连接GitHub远程仓库的权限,包括生成密钥、添加到GitHub账户以及验证连接等步骤。 ... [详细]
  • 本文介绍如何配置SecureCRT以正确显示Linux终端的颜色,并解决中文显示问题。通过简单的步骤设置,可以显著提升使用体验。 ... [详细]
  • 最新计算机专业原创毕业设计参考选题都有源码+数据库是近期作品ling取参考你的选题刚好在下面有,有时间看到机会给您发1ssm资源循环利用2springboot校园考勤系统3ssm防 ... [详细]
  • 本指南详细介绍了如何在同一台计算机上配置多个GitHub账户,并使用不同的SSH密钥进行身份验证,确保每个账户的安全性和独立性。 ... [详细]
author-avatar
GodlikeZ寰
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有