Android中如何优雅的处理重复点击实例代码
问题
有时候有些操作是防止用户在一次响应结束中再响应下一个。但有些测试用户就要猛点,狂点。像这种恶意就要进行防止。
比如在客户端中,一些按钮一般是需要避免重复点击的,比如:购买丶支付丶确定丶提交丶点赞丶收藏等等场景,这些场景短时间内的重复点击会引发一些问题.
下面话不多说了,来一起看看详细的介绍吧
以前的处理方式
可能是采用手动记录最后的点击时间,再通过计算时间间隔来判断是否重复点击
private long mlastclicktime = 0; public static final int time_interval = 1000; private button mbutton; private void initview() { mbutton.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { if (system.currenttimemillis() - mlastclicktime >= time_interval) { //to do mlastclicktime = system.currenttimemillis(); } else { toast.maketext(getactivity(), "请勿重复点击", toast.length_long).show(); } } }); }
或者封装一下采用抽象处理
public abstract class iclicklistener implements view.onclicklistener { private long mlastclicktime = 0; public static final int time_interval = 1000; @override public final void onclick(view v) { if (system.currenttimemillis() - mlastclicktime >= time_interval) { oniclick(v); mlastclicktime = system.currenttimemillis(); } else { onagain(v); } } protected abstract void oniclick(view v); protected void onagain(view v) { } }
使用(无需提醒重复点击)
mbutton.setonclicklistener(new iclicklistener() { @override protected void oniclick(view v) { } });
或者(需提醒重复点击)
mbutton.setonclicklistener(new iclicklistener() {
@override
protected void oniclick(view v) {
}
@override
protected void onagain(view v) {
}
});
可以看到经过封装之后,使用起来还是很方便的,但是有几个缺点
- 侵入性过大-onclicklistener全部替换为子类iclicklistener
- 不可逆-不能很方便的还原为onclicklistener,因为不是同个回调
- 如果是第三方控件则无法处理重复点击
- 只能写成内部类方式-由于单继承特性,我们只能内部类回调,代码不美观
优雅的处理方式
重复点击的问题其实是如何动态控制原有的点击事件是否产生,而不是在原有的点击事件上增强功能;结合设计模式可以知道,代理模式可以很好的处理这种问题,而不是继承.
代理
public class clickproxy implements view.onclicklistener { private view.onclicklistener origin; private long lastclick = 0; private long timems = 1000; public clickproxy(view.onclicklistener origin) { this.origin = origin; } @override public void onclick(view v) { if (system.currenttimemillis() - lastclick >= timems) { origin.onclick(v); lastclick = system.currenttimemillis(); } } }
原先的点击事件
mbutton.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { //to do } });
代理使用
mbutton.setonclicklistener(new clickproxy(new view.onclicklistener() { @override public void onclick(view v) { //to do } }));
可以看到,原有代码逻辑没有改动,只是添加了代理类,这样大大减小了侵入性
当然还可以扩展一下,提供重复点击的回调和自定义间隔时间,增加一个构造函数
public class clickproxy implements view.onclicklistener { private view.onclicklistener origin; private long lastclick = 0; private long timems = 1000; //ms private iagain miagain; public clickproxy(view.onclicklistener origin, long timems, iagain again) { this.origin = origin; this.miagain = again; this.timems = timems; } public clickproxy(view.onclicklistener origin) { this.origin = origin; } @override public void onclick(view v) { if (system.currenttimemillis() - lastclick >= timems) { origin.onclick(v); lastclick = system.currenttimemillis(); } else { if (miagain != null) miagain.onagain(); } } public interface iagain { void onagain();//重复点击 } }
如何处理第三方view内部的点击事件
可能我们使用一个自定义控件,他的内部已经消费了点击事件,但是需要避免重复点击,我们不可能去改内部的代码,也不能重新设置点击事件,那样会丢失内部的处理逻辑;这时可以采用反射的处理方式,再结合代理来实现无缝替换
//提供一个静态方法 public class clickfilter { public static void setfilter(view view) { try { field field = view.class.getdeclaredfield("mlistenerinfo"); field.setaccessible(true); class listinfotype = field.gettype(); object listinfo = field.get(view); field onclickfield = listinfotype.getfield("monclicklistener"); view.onclicklistener origin = (view.onclicklistener) onclickfield.get(listinfo); onclickfield.set(listinfo, new clickproxy(origin)); } catch (exception e) { e.printstacktrace(); } } }
使用:
private statebutton mstatebutton;//自定义控件 private void initview() { clickfilter.setfilter(mstatebutton); }
这种动态替换的方式同样适合普通场景,在设置点击事件后,都可以通过设置该过滤器来处理重复点击(包括butterknife等注解绑定的点击事件)
最后
ok.以上就是讨论如何优雅处理重复点击的全部内容,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。