Android优雅地处理按钮重复点击的几种方法
app中,有很大一部分场景是点击按钮,向服务端提交数据,由于网络请求需要时间,用户很可能会多次点击,造成数据重复提交,造成各种莫名其妙的问题。
因此,防止按钮多次点击,是android开发中一个很重要的技术手段。
以前的处理方式
网上查找到的,或者你可能会想到的方法大概有这些:
1.每个按钮点击事件中,记录点击时间,判断是否超过点击时间间隔
private long mlastclicktime = 0; public static final long time_interval = 1000l; private button bttest; private void initview() { bttest = findviewbyid(r.id.bt_test); bttest.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { long nowtime = system.currenttimemillis(); if (nowtime - mlastclicktime > time_interval) { // do something mlastclicktime = nowtime; } else { toast.maketext(mainactivity.this, "不要重复点击", toast.length_short).show(); } } }); }
这种方式,每个点击事件都需要写一个时间判断,重复代码很多。
2.封装一个点击事件,处理点击间隔判断
public abstract class customclicklistener implements view.onclicklistener { private long mlastclicktime; private long timeinterval = 1000l; public customclicklistener() { } public customclicklistener(long interval) { this.timeinterval = interval; } @override public void onclick(view v) { long nowtime = system.currenttimemillis(); if (nowtime - mlastclicktime > timeinterval) { // 单次点击事件 onsingleclick(); mlastclicktime = nowtime; } else { // 快速点击事件 onfastclick(); } } protected abstract void onsingleclick(); protected abstract void onfastclick(); }
使用:
bttest.setonclicklistener(new customclicklistener() { @override protected void onsingleclick() { log.d("xxx", "onsingleclick"); } @override protected void onfastclick() { log.d("xxx", "onfastclick"); } });
相比于第一种方式,这种方法将重复点击的判断封装在customclicklistener内部,外部无需处理时间判断,只需要实现点击方法即可。
3.利用rxandroid处理重复点击
rxview.clicks(view) .throttlefirst(1, timeunit.seconds) .subscribe(new consumer<object>() { @override public void accept(object o) throws exception { // do something } });
响应式地处理按钮点击,利用rxjava的操作符,来防止重复点击,相较于第1,2方案来说,此方法更为优雅一些。
思考一下:
这三种方法,不论哪一种,都对原有点击事件有很大的侵入性,要么你需要往click事件中加方法,要么你需要替换整个click事件,那么,有没有一种方式,可以在不改动原有逻辑的情况下,又能很好地处理按钮的重复点击呢?
更为优雅的处理方式
往同一类型的所有方法,都加上统一的处理逻辑,我们很快就能想到一个词: aop ,没错, 面向切面编程 。
如何使用aop来解决重复点击问题?
1.引入aspectj
android 上使用aop编程,一般使用aspectj这个库
站在巨人的肩膀上,沪江已经开源了aspectj的gradle插件,方便我们使用aspectj
在项目根目录下的build.gradle中,添加依赖:
dependencies { ...... classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0' }
在app或其他module目录下的build.gradle中,添加:
apply plugin: 'android-aspectjx' dependencies { ...... implementation 'org.aspectj:aspectjrt:1.8.9' }
2.添加一个自定义注解
@retention(retentionpolicy.class) @target(elementtype.method) public @interface singleclick { /* 点击间隔时间 */ long value() default 1000; }
添加自定义注解的原因是,方便管理哪些方法使用了重复点击的aop,同时可以在注解中传入点击时间间隔,更加灵活。
3.封装一个重复点击判断工具类
public final class xclickutil { /** * 最近一次点击的时间 */ private static long mlastclicktime; /** * 最近一次点击的控件id */ private static int mlastclickviewid; /** * 是否是快速点击 * * @param v 点击的控件 * @param intervalmillis 时间间期(毫秒) * @return true:是,false:不是 */ public static boolean isfastdoubleclick(view v, long intervalmillis) { int viewid = v.getid(); long time = system.currenttimemillis(); long timeinterval = math.abs(time - mlastclicktime); if (timeinterval < intervalmillis && viewid == mlastclickviewid) { return true; } else { mlastclicktime = time; mlastclickviewid = viewid; return false; } } }
4.编写aspect aop处理类
@aspect public class singleclickaspect { private static final long default_time_interval = 5000; /** * 定义切点,标记切点为所有被@singleclick注解的方法 */ @pointcut("execution(@me.baron.test.annotation.singleclick * *(..))") public void methodannotated() {} /** * 定义一个切面方法,包裹切点方法 */ @around("methodannotated()") public void aroundjoinpoint(proceedingjoinpoint joinpoint) throws throwable { // 取出方法的参数 view view = null; for (object arg : joinpoint.getargs()) { if (arg instanceof view) { view = (view) arg; break; } } if (view == null) { return; } // 取出方法的注解 methodsignature methodsignature = (methodsignature) joinpoint.getsignature(); method method = methodsignature.getmethod(); if (!method.isannotationpresent(singleclick.class)) { return; } singleclick singleclick = method.getannotation(singleclick.class); // 判断是否快速点击 if (!xclickutil.isfastdoubleclick(view, singleclick.value())) { // 不是快速点击,执行原方法 joinpoint.proceed(); } } }
使用方法
private void initview() { bttest = findviewbyid(r.id.bt_test); bttest.setonclicklistener(new view.onclicklistener() { @singleclick @override public void onclick(view v) { // do something } }); }
只需要一个注解,即完成了按钮的防止重复点击,其他所有工作交给编译器,代码清爽了很多有木有。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。