动态代理实现控件点击事件注入

概述

本文主要分享基于动态代理实现控件点击事件注入。

实现思路:

  • 自定义EventType注解标记需要点击事件类型
  • 自定义OnClick、OnLongClick注解标记点击、长按事件
  • 扫描页面中的注解,判断该注解是否被EventType注解标记
  • 获取EventType注解的事件类型通过动态代理创建对应的事件代理对象
  • 查找被OnClick、OnLongClick标示的控件并通过反射注入事件代理对象完成事件注入

自定义EventType、OnClick、OnLongClick注解

//这是一个元注解:即可以用在注解之上的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface EventType {

    Class listenerType();

    String listenerSetter();

}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventType(listenerType = View.OnClickListener.class,listenerSetter = "setOnClickListener")
public @interface OnClick {

    int []value();

}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventType(listenerType = View.OnLongClickListener.class,listenerSetter = "setOnLongClickListener")
public @interface OnLongClick {

    int []value();

}

动态代理实现点击事件注入

关键代码如下:

 public static void injectEvent(final MainActivity activity) {

        //注意:反射是基于Class
        Class activityClass = activity.getClass();
        //获取所有的方法
        Method[] declaredMethods = activityClass.getDeclaredMethods();

        for (final Method declaredMethod : declaredMethods) {
            //获取方法声明的注解数组,Annotation是所有注解的父接口
            Annotation[] annotations = declaredMethod.getAnnotations();

            for (Annotation annotation : annotations) {//OnClick、OnLongClick等

                //获取注解类型
                Class annotationType = annotation.annotationType();
                //A.isAnnotationPresent(B.class)即注释B是否在此A上
                if (annotationType.isAnnotationPresent(EventType.class)) {

                    //获取EventType注解
                    EventType eventType = annotationType.getAnnotation(EventType.class);

                    //获取EventType注解声明的参数
                    Class listenerType = eventType.listenerType();
                    String listenerSetter = eventType.listenerSetter();

                    try {

                        Method valueMethod = annotationType.getDeclaredMethod("value");
                        //获取控件Id数组
                        int[] ids = (int[]) valueMethod.invoke(annotation);

                        declaredMethod.setAccessible(true);

                        //创建事件回调动态代理类,省去判断onClick、onLongClick
                        Object proxyInstance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, new InvocationHandler() {
                            //动态代理回调方法
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                return declaredMethod.invoke(activity, args);
                            }
                        });

                        for (int id : ids) {
                            //查处所有Id对应的控件
                            View view = activity.findViewById(id);

                            //获取指定的点击方法如:setOnClickListener、setOnLongClickListener
                            Method setMethod = view.getClass().getMethod(listenerSetter,listenerType);
                            //反射调用控件的setOnClickListener、setOnLongClickListener方法,进而调用到代理类的方法
                            setMethod.invoke(view, proxyInstance);
                        }

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

页面注入

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "fmt";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectUtils.injectEvent(this);
    }

    @OnClick({R.id.btn1, R.id.btn2})
    public void click(View view) {
        switch (view.getId()) {
            case R.id.btn1:
                Log.e(TAG, "click: 按钮1");
                break;
            case R.id.btn2:
                Log.e(TAG, "click: 按钮2");
                break;
        }
    }


    @OnLongClick({R.id.btn1, R.id.btn2})
    public boolean longClick(View view) {
        switch (view.getId()) {
            case R.id.btn1:
                Log.e(TAG, "longClick: 按钮1");
                break;
            case R.id.btn2:
                Log.e(TAG, "longClick: 按钮2");
                break;
        }
        return false;
    }
}

完整代码实现

百度链接

密码:9ln0

你可能感兴趣的:(动态代理实现控件点击事件注入)