代理处理类: 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);
}