本文主要给大家分享如何在全局上去监听 click 点击事件,并做些通用处理或是拦截。使用场景可能就是具体的全局防快速重复点击,或是通用打点分析上报,用户行为监控等。以下将以四种不同的思路和实现方式去监控全局的点击操作,由简单到复杂逐一讲解。
方式一,适配监听接口,预留全局处理接口并作为所有监听器的基类使用
抽象出公共基类监听对象,可预留拦截机制和通用点击处理,简要代码如下:
public abstract class CustClickListener implements View.OnClickListener{
@Override
public void onClick(View view) {
if(!interceptViewClick(view)){
onViewClick(view);
}
}
protected boolean interceptViewClick(View view){
//TODO:这里可做一此通用的处理如打点,或拦截等。
return false;
}
protected abstract void onViewClick(View view);
}
使用方式之一匿名对象作为公共监听器
CustClickListener mClickListener = new CustClickListener() {
@Override
protected void onViewClick(View view) {
Toast.makeText(CustActvity.this, view.toString(), Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
findViewById(R.id.button).setOnClickListener(mClickListener);
}
这种方式比较简单,无兼容问题,但是需要自始至终都要使用基于基类的监听器对象,对开发者约束比较大。适用于新项目之初就有此使用约定。对于老代码重构工作量比较大,而且如果接入第三方墨盒模块就无能为力了。
方式二,反射代理,适时偷梁换柱开发者无感知,在适配包装器里做通用处理。
以下是代理接口和内置监听适配器,全局的监听接口需要实现IProxyClickListener
并设置到内置适配器WrapClickListener
里
public interface IProxyClickListener {
boolean onProxyClick(WrapClickListener wrap, View v);
class WrapClickListener implements View.OnClickListener {
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) {
mBaseListener.onClick(v);
}
}
}
}
我们需要选择一个时机对所有设置有监听器的 View
做监听代理的 hook .这个时机可以对 Activity 的根View添加一个视图变化监听(当然也可选择在 Activity 的 DOWN 事件的分发时机):
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
hookViews(rootView, 0)
}
});
注:以上为了方便匿名注册了监听,实际使用在 Activity 退出时要反注册掉。
在进行代理前先要反射获取View监听器相关的 Method 和 Field 对象如下:
public void init() {
if (sHookMethod == null) {
try {
Class vi