<Excerpt in index | 首页摘要>
Hook View OnClickListener 实现避免重复点击
<The rest of contents | 余下全文>
概述
做Android开发,经常碰到测试反馈一个问题。
重复点击xx,打开(执行了)2次操作。
自然这种情况是需要一定的避免的。如何避免呢?
我们可以通过Rx来设置点击事件的方式,增加重复点击时间。
我们可以通过封装OnCLickListener,每次设置点击事件,用我们的代理OnCLickListener(内部封装点击时间验证)来完成。
当然,还有这种方式,HookView的OnclickListener来实现。
(弊端,如果是类似于网络请求之后再设置的点击事件,这种具有延时的设置,可能会失效)
原理
View所有的事件都放在mListenerInfo这个成员变量里面
通过反射获取View身上的getListenerInfo()方法,可以获取mListenerInfo对象
然后遍历所有的View
遍历每个View身上的mListenerInfo,找到OnClickListener。
通过代理模式,创建一个我们自己的增加了重复点击判断的OnCliCkListenerProxy
然后替换原来的。
代码实现
Rx、代理这里就不具体展示了。
先看代理OnClickListener
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package com.microcity.microbus_go.listeners;
import android.view.View;
import java.util.Calendar;
public class OnClickListenerProxy implements View.OnClickListener {
private static final int MIN_CLICK_DELAY_TIME = 500; private long lastClickTime = 0;
public View.OnClickListener target;
public OnClickListenerProxy(View.OnClickListener target) { this.target = target; }
@Override public void onClick(View v) { long currentTime = Calendar.getInstance().getTimeInMillis(); if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) { lastClickTime = currentTime; if (target != null) target.onClick(v); } } }
|
这个没有什么太大的难度,很简单。就是增加了一个时间的验证。
然后创建一个HookViewUtils类
这个类负责找到所有的View,然后反射修改OnCLickListener。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| package com.microcity.microbus_go.utils;
import android.app.Activity; import android.view.View; import android.view.ViewGroup;
import com.microcity.microbus_go.listeners.OnClickListenerProxy;
import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List;
public class HookViewUtils { public static void hook(Activity mAct) { try { Class viewClazz = Class.forName("android.view.View"); Method listenerInfoMethod = viewClazz.getDeclaredMethod("getListenerInfo"); if (!listenerInfoMethod.isAccessible()) { listenerInfoMethod.setAccessible(true); }
List<View> list = getAllChildViews(mAct.getWindow().getDecorView());
for (View view : list) { Object listenerInfoObj = listenerInfoMethod.invoke(view);
Class listenerInfoClazz = Class.forName("android.view.View$ListenerInfo");
Field onClickListenerField = listenerInfoClazz.getDeclaredField("mOnClickListener");
if (!onClickListenerField.isAccessible()) { onClickListenerField.setAccessible(true); } View.OnClickListener mOnClickListener = (View.OnClickListener) onClickListenerField.get(listenerInfoObj); View.OnClickListener onClickListenerProxy = new OnClickListenerProxy(mOnClickListener); onClickListenerField.set(listenerInfoObj, onClickListenerProxy); } } catch (Exception e) { e.printStackTrace(); } }
private static List<View> getAllChildViews(View view) { List<View> allchildren = new ArrayList<View>(); if (view instanceof ViewGroup) { ViewGroup vp = (ViewGroup) view; for (int i = 0; i < vp.getChildCount(); i++) { View viewchild = vp.getChildAt(i); allchildren.add(viewchild); allchildren.addAll(getAllChildViews(viewchild)); } } return allchildren; }
}
|
第三步:
找到你的BaseActivity
在其onCreate中加入:
1
| getWindow().getDecorView().postDelayed(() -> { HookViewUtils.hook(this) }, 300);
|
至此,就大功告成了,快去试试看吧~