阅读 175

全局点击事件监听拦截

起因

最近发现一个快速点击的问题,纵使有RxView.clicks。或者其他的方法,但是每次要在setOnClickListener里处理,而且老是忘加。得有一个全局的懒人神器来解决。

思路

首先肯定是放在BaseActivity里的,用反射去获取setOnClickListener的事件,并且(动态)代理这个接口。
自定义注解+反射?那还是得每个都添注解,不符合要求。
1.对DecorView的视图树遍历,这样就能得到,页面所有的View。
2.根据View获取到View.OnClickListener
3.利用反射 将View的mOnClickListener,设置成我们自己的代理mOnClickListener(做一层拦截)Field.set()

实现

  1. 观察setOnClickListener

其实就是将new的OnClickListener赋值给了getListenerInfo()的mOnClickListener

image.png image.png

  1. 获取View,可以通过遍历根布局,获取当前页的所有View,也就是遍历DecorView

image.png

RecyclerView 的回收复用机制,会清空item,导致没法这样操作,要过滤掉 image.png

  1. 为OnClickListener设置代理

image.png

想要mOnClickListener得取到ListenerInfo -->  也就是getListenerInfo()
mOnClickListener是ListenerInfo的一个成员变量。
ListenerInfo是View的一个内部类,也就是getListenerInfo()。 image.png

所以

image.png

这时候已经获取到了View.OnClickListener,得为他设置代理,然后中间插入我们的代码。

image.png

image.png

补充:View设置过了,就为他设置一个int的tag,标记,防止重复的设置
view.setTag(mPrivateTagKey, recycledContainerDeep);

完整代码

public class ViewHook {     IProxyClickListener mInnerClickProxy;     Field sHookField;     Method sHookMethod;     //是否已经设置了代理     final int mPrivateTagKey = 424;     public void initHookClick() {         if (sHookMethod == null) {             try {                 Class viewClass = Class.forName("android.view.View");                 if (viewClass != null) {                     sHookMethod = viewClass.getDeclaredMethod("getListenerInfo");                     if (sHookMethod != null) {                         sHookMethod.setAccessible(true);                     }                 }             } catch (Exception e) {                 reportError(e, "init");             }         }         if (sHookField == null) {             try {                 Class listenerInfoClass = Class.forName("android.view.View$ListenerInfo");                 if (listenerInfoClass != null) {                     sHookField = listenerInfoClass.getDeclaredField("mOnClickListener");                     if (sHookField != null) {                         sHookField.setAccessible(true);                     }                 }             } catch (Exception e) {                 reportError(e, "init");             }         }     }     private void hookClickListener(View view, int recycledContainerDeep, boolean forceHook) {         boolean needHook = forceHook;         if (!needHook) {             needHook = view.isClickable();             if (needHook && recycledContainerDeep == 0) {                 needHook = view.getTag(mPrivateTagKey) == null;             }         }         if (needHook) {             try {                 Object getListenerInfo = sHookMethod.invoke(view);                 View.OnClickListener baseClickListener = getListenerInfo == null ?                         null : (View.OnClickListener) sHookField.get(getListenerInfo);//获取已设置过的监听器                 if ((baseClickListener != null && !(baseClickListener instanceof IProxyClickListener.WrapClickListener))) {                     sHookField.set(getListenerInfo, new IProxyClickListener.WrapClickListener(baseClickListener, mInnerClickProxy));                     view.setTag(mPrivateTagKey, recycledContainerDeep);                 }             } catch (Exception e) {                 reportError(e, "hook");             }         }     }     public void hookViews(View view, int recycledContainerDeep) {         if (view.getVisibility() == View.VISIBLE) {             boolean forceHook = recycledContainerDeep == 1;             if (view instanceof ViewGroup) {                 boolean existAncestorRecycle = recycledContainerDeep > 0;                 ViewGroup p = (ViewGroup) view;                 if (!(p instanceof AbsListView || p instanceof RecyclerView) || existAncestorRecycle) {                     hookClickListener(view, recycledContainerDeep, forceHook);                     if (existAncestorRecycle) {                         recycledContainerDeep++;                     }                 } else {                     recycledContainerDeep = 1;                 }                 int childCount = p.getChildCount();                 for (int i = 0; i < childCount; i++) {                     View child = p.getChildAt(i);                     hookViews(child, recycledContainerDeep);                 }             } else {                 hookClickListener(view, recycledContainerDeep, forceHook);             }         }     }     private void reportError(Exception e, String init) {         Log.e(TAG, init);     }     public void onDestroy() {         sHookField = null;         sHookMethod = null;         mInnerClickProxy = null;     } } 复制代码

public interface IProxyClickListener {     boolean onProxyClick(WrapClickListener wrap, View v);     class WrapClickListener implements View.OnClickListener {         public static int oldId = -1;         public static long lastClickTime;         IProxyClickListener mProxyListener;         View.OnClickListener mBaseListener;         public WrapClickListener(View.OnClickListener l, IProxyClickListener proxyListener) {             mBaseListener = l;             mProxyListener = proxyListener;         }         @Override         public void onClick(View v) {             boolean handled = mProxyListener == null ? false : mProxyListener.onProxyClick(WrapClickListener.this, v);             if (!handled && mBaseListener != null) {                 if (avoidDoubleClick(v)){                     mBaseListener.onClick(v);                 }             }         }         /**          * 快速点击拦截          */         public boolean avoidDoubleClick(View v) {             int viewId = v.getId();             if (oldId == -1) {                 lastClickTime = SystemClock.elapsedRealtime();                 oldId = viewId;             } else if (viewId == oldId) {                 long time = SystemClock.elapsedRealtime();                 if (time - lastClickTime < 1000) {                     return false;                 }                 lastClickTime = SystemClock.elapsedRealtime();             } else {                 lastClickTime = SystemClock.elapsedRealtime();                 oldId = viewId;             }             return true;         }     } } 复制代码

public abstract class BaseActivity extends AppCompatActivity {     ViewHook viewHook;     @Override     protected void onCreate(@Nullable Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(getLayoutId());         init();         viewHook = new ViewHook();         viewHook.initHookClick();         getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {             @Override             public void onGlobalLayout() {                 viewHook.hookViews(getWindow().getDecorView(), 0);             }         });     }     abstract public int getLayoutId();     abstract public void init();     @Override     protected void onDestroy() {         super.onDestroy();         viewHook.onDestroy();         viewHook = null;     } }


作者:铁头娃wawa
链接:https://juejin.cn/post/7021056193439203335


文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐