Android反射+动态代理 捕捉所有点击事件(View.OnCLickListener,AbsListView.OnItemClickListener)

代理处理类: ProxyClickHandler

使用: ProxyClickHandler.getInstance().hookViews(layoutView);


import android.annotation.SuppressLint;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;

import com.android.javaporject.R;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Objects;

/**
 * <pre>
 * Created by DengDongQi on 2021/4/15
 *
 * 反射+动态代理  统计activity下所有view的OnClickListener
 *
 * </pre>
 */
public class ProxyClickHandler {
    
    private static final String TAG = "CLICK ------>";

    private ProxyClickHandler(){}

    private final static  class InstanceHolder{
        private final static ProxyClickHandler instance = new ProxyClickHandler();
    }

    public static ProxyClickHandler getInstance(){
        return InstanceHolder.instance;
    }

    //代理标识
    private final static String VIEW_PROXY_TAG = "proxy";


    /**
     * 遍历view进行hook
     * */
    public void hookViews(View rootView){
        if (rootView!=null && rootView.getVisibility() == View.VISIBLE) {
            if (rootView instanceof ViewGroup) {
                hookClickListener(rootView);
                int childCount = ((ViewGroup) rootView).getChildCount();
                for (int i = 0; i<childCount;i++) {
                    View child = ((ViewGroup) rootView).getChildAt(i);
                    hookViews(child);
                }
            } else {
                hookClickListener(rootView);
            }
        }
    }

    /**
     * hook view 的点击事件
     * */
    private void hookClickListener(View view) {
        if(isProxyClickListener(view)){
            return;
        }

        try {
            //region AbsListView listview GridView 设置mOnItemClickListener代理
            if (view instanceof AbsListView) {
                Class adapterViewClass = Class.forName("android.widget.AdapterView");
                // 反射获取AbsListView 内部类mOnItemClickListener
                Field oiclField = adapterViewClass.getDeclaredField("mOnItemClickListener");
                oiclField.setAccessible(true);
                AdapterView.OnItemClickListener listener = (AdapterView.OnItemClickListener)oiclField.get(view);
                //动态代理listener
                if(listener!=null){
                    oiclField.set(view,proxyClickListener(listener));
                    //proxy标识  ids.xml -> <item type="id" name="tag_proxy"/>
                    view.setTag(R.id.tag_proxy,VIEW_PROXY_TAG);
                }
            }
            //endregion

            //region 其他View
            else{
                // 反射获取View方法 getListenerInfo
                Class viewClass = Class.forName("android.view.View");
                Method getListenerInfo = viewClass.getDeclaredMethod("getListenerInfo");
                getListenerInfo.setAccessible(true);
                //View.ListenerInfo
                Object listenerInfo = getListenerInfo.invoke(view);
                // 反射获取View 内部类ListenerInfo中的OnClickListener
                if(listenerInfo!=null) {
                    Class lInfoClass = Class.forName("android.view.View$ListenerInfo");
                    Field oclField = lInfoClass.getDeclaredField("mOnClickListener");
                    oclField.setAccessible(true);
                    View.OnClickListener listener = (View.OnClickListener) oclField.get(listenerInfo);
                    //动态代理listener
                    if(listener!=null){
                        oclField.set(listenerInfo,proxyClickListener(listener));
                        //proxy标识
                        view.setTag(R.id.tag_proxy,VIEW_PROXY_TAG);
                    }
                }
            }
            //endregion

        }catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     * view是否动态代理了点击事件
     * */
    public boolean isProxyClickListener(View view) {
        return  view != null  && Objects.equals(view.getTag(R.id.tag_proxy),VIEW_PROXY_TAG);
    }

    /**
     * 动态代理
     * */
    public Object proxyClickListener(Object target){

        InvocationHandler invocationHandler = new InvocationHandler() {
            @SuppressLint("ResourceType")
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Log.e(TAG,"点击前");
                Object obj = method.invoke(target,args);
                Log.e(TAG,"点击后");
                if(args!=null && args.length>0) {
                    for (Object arg : args) {
                        if (arg instanceof View) {
                            //TODO 统计点击 ID/次数/频率
                            try {
                                if(((View) arg).getId()>0) {
                                    Log.e(TAG, "点击了 View == " + ((View) arg).getContext().getResources().getResourceEntryName(((View) arg).getId()));
                                }
                            }catch (Exception e){
                                //动态生成的view没设置id
                                e.printStackTrace();
                            }
                        }
                    }
                }
                return obj;
            }
        };

        Object listener = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                invocationHandler);

        return listener;
    }

    /**
     * 清除view 代理标识tag 用于adapter
     * */
    public void clearViewTag(View rootView){
        if (rootView instanceof ViewGroup) {
            rootView.setTag(R.id.tag_proxy,null);
            int childCount = ((ViewGroup) rootView).getChildCount();
            for (int i = 0; i<childCount;i++) {
                View child = ((ViewGroup) rootView).getChildAt(i);
                clearViewTag(child);
            }
        } else {
            rootView.setTag(R.id.tag_proxy,null);
        }
    }


}

Activity超类 : BaseActivity

利用 ViewTreeObserver.OnGlobalLayoutListener监听视图树的布局发生改变时,处理代理View点击事件

import android.os.Bundle;
import android.view.View;
import android.view.ViewTreeObserver;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

/**
 * <pre>
 * Created by DengDongQi on 2021/3/23
 * activity超类
 * </pre>
 */
public class BaseActivity extends AppCompatActivity {
    MyGlobalLayoutListener myGlobalLayoutListener;
    private View mDecorView;

    @Override
    protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        myGlobalLayoutListener = new MyGlobalLayoutListener();
        mDecorView = getWindow().getDecorView();
        mDecorView.getViewTreeObserver().addOnGlobalLayoutListener(myGlobalLayoutListener);
    }

    class MyGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener{
        @Override
        public void onGlobalLayout() {
            //视图树的布局发生改变时,处理代理View点击事件
            ProxyClickHandler.getInstance().hookViews(mDecorView);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(myGlobalLayoutListener!=null && mDecorView!=null){
            mDecorView.getViewTreeObserver().removeOnGlobalLayoutListener(myGlobalLayoutListener);
        }
    }
}

AbsListView adpater超类: BaseAbsAdapter 

adapter的getVIew最后处理代理View点击事件

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

import androidx.annotation.LayoutRes;

import java.util.List;

/**
 * <pre>
 * Created by DengDongQi on 2021/4/15
 * AbsListView adpater超类
 * </pre>
 */
public abstract class BaseAbsAdapter<T> extends BaseAdapter {
    private Context context;
    private List<T> data;

    public BaseAbsAdapter(Context context,List<T> data) {
        this.context = context;
        this.data = data;
    }

    public List<T> getData() {
        return data;
    }

    public void setData(List<T> data) {
        this.data = data;
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public Object getItem(int position) {
        return data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    public abstract @LayoutRes int setViewLayoutId();

    public abstract BaseAbsHolder setViewHolder(View view);

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        BaseAbsHolder viewHolder;
        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(setViewLayoutId(), null);
            viewHolder = setViewHolder(convertView);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (BaseAbsHolder) convertView.getTag();
        }
        ProxyClickHandler.getInstance().clearViewTag(convertView);
        viewHolder.setData(position);
        //处理代理View点击事件
        ProxyClickHandler.getInstance().hookViews(convertView);

        return convertView;
    }

    public abstract class BaseAbsHolder {
        public View mRootView;
        BaseAbsHolder(View view){
            this.mRootView = view;
            initView();
        }

        public abstract void initView();

        public abstract void setData(int position);
    }
}

RecyclerView adapter超类:BaseRvAdapter

adapter 中onBindViewHolder 最后处理代理View点击事件

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

/**
 * <pre>
 * Created by DengDongQi on 2021/4/15
 * RecyclerView adapter超类
 * </pre>
 */
public abstract class BaseRvAdapter<T> extends RecyclerView.Adapter<BaseRvAdapter.BaseRvHolder> {
    private Context context;
    private List<T> data;

    private OnItemClickListener listener;

    public BaseRvAdapter(Context context, List<T> data) {
        this.context = context;
        this.data = data;
    }

    public void setData(List<T> data) {
        this.data = data;
        this.notifyDataSetChanged();
    }


    public List<T> getData() {
        return data;
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }

    public abstract int getItemViewType(int position,T data);

    public abstract @LayoutRes int setViewLayoutId(int viewType);

    public abstract BaseRvHolder setViewHolder(View view,int viewType);

    @NonNull
    @Override
    public BaseRvHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(setViewLayoutId(viewType), parent, false);
        return setViewHolder(view,viewType);
    }

     @Override
    public void onBindViewHolder(@NonNull BaseRvHolder holder, final int position) {

        ProxyClickHandler.getInstance().clearViewTag(holder.itemView);

        holder.setData(position);
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (listener != null) {
                    listener.onItemClick(position);
                }
            }
        });
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if(listener!=null){
                    listener.onItemLongClick(position);
                }
                return true;
            }
        });

        ProxyClickHandler.getInstance().hookViews(holder.itemView);
     }

    @Override
    public int getItemViewType(int position) {
        if(data!=null && data.get(position)!=null){
            return getItemViewType(position,data.get(position));
        }
        return super.getItemViewType(position);
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

    public interface OnItemClickListener {
        void onItemClick(int position);
        void onItemLongClick(int postion);
    }

    public static abstract class BaseRvHolder extends RecyclerView.ViewHolder {
        public View mRootView;
        public BaseRvHolder(View itemView) {
            super(itemView);
            mRootView = itemView;
            initView();
        }

        public abstract void initView();

        public abstract void setData(int position);
    }

}

Dialog,PopupWindow 超类重写show() 方法,加上处理代理View点击事件



    private View layoutView;

    //......

    @Override
    public void setContentView(View view) {
        super.setContentView(view);
        layoutView = view;
    }

    @Override
    public void show() {
        super.show();
        ProxyClickHandler.getInstance().hookViews(layoutView);
    }

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值