动态代理之注解实现控件的点击事件

比如我们在Activity里面不想通过

        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                
            }
        });
        view.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                return false;
            }
        });

现在只我们只需要定义一个OnClick注解接口就可以实现

先来分析一下上面二个方法有什么共同点

1):方法名:(setOnClickListener,setOnLongClickListener)

2):方法参数:(View.OnClickListener,View.OnLongClickListener),二个接口

3):执行方法:(onClick,onLongClick)

有了这些共同点就好办,

第一: 我们定义一个事件注解 EventBase

@Target(ElementType.ANNOTATION_TYPE)//放在注解之上
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {

    String listenerSetter();// 监听的方法名 比如: setOnClickListener()

    Class<?> listenerType(); //  获取到需要监听的类 比如: View.OnClickListener

    String callBackListener();//比如 onClick()
}

第二: 我们再来定义一个OnClick注解

@Target(ElementType.METHOD)//注解在方法之上
@Retention(RetentionPolicy.RUNTIME) //运行时执行
@EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, callBackListener = "onClick") //上面写的注解类,注解在注解之上的
public @interface OnClick {
    int[] value();
}

第三: 接下来我们就来写一些自动生成点击事件了

定义方法

private static void injectEvents(Activity activity) {} //activity
public static void injectEvents(Fragment fragment,View contentView){} //fragment

二个基本实现一样,就以activity为例

private static void injectEvents(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();//获取到类
        Method[] methods = clazz.getDeclaredMethods();//获取到该activity类下所有的公有或者私有的方法,不包括父类

         for (Method method : methods) {//对方法一个一个遍历
            Annotation[] annotations = method.getAnnotations();//获取到方法上所有的注解,因为有可能有多个注解,比如上注解上加注解
            for (Annotation annotation : annotations) {//遍历获取到的注解集合
                Class<? extends Annotation> annotationType = annotation.annotationType();//获取到注解上的类型
                if (annotationType != null) {
                    EventBase eventBase = annotationType.getAnnotation(EventBase.class);//获取到注解上的注解,如果是我们自己写的@EventBase的时候往下走,不是则下一循环
                    if (eventBase != null) {
                        //获取到了注解上的注解的一些参数 
                        String listenerSetter = eventBase.listenerSetter(); // setOnClickListener 
                        String callBackListener = eventBase.callBackListener(); // onClick
                        Class<?> listenerType = eventBase.listenerType();// interface android.view.View$OnClickListener
                        //接下来就是要实现我们怎么动态代理view里面的点击事件了
                         //创建一个带有目标activity对象的代理handler  将我们对象里面的method 方法进行添加
                        ListenerInvocationHandler handler = new ListenerInvocationHandler(activity);
                        handler.addMethod(callBackListener, method);
                        
                        //这句代码是获取到了View.OnclickListener对象
                        Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler);//比如每当我们调用了listenerType里面的方法时,就会进入到handler类里的invoke方法里.

                        try {
                            Method valueMethod = annotationType.getDeclaredMethod("value");//annotationType就是我们定义的@OnClick注解接口 这名话的意思就是获取到接口里面的value方法
                            int[] viewIds = (int[]) valueMethod.invoke(annotation);//通过反射获取到的valueMethod方法来获取到返回的数据,anotation就是我们的注解类的实例
                            for (int viewId : viewIds) {
                                View view = activity.findViewById(viewId);
                                Method m = view.getClass().getMethod(listenerSetter, listenerType);//获取到View.setOnclickListener()方法

                                m.invoke(view, listener);//执行的是view.setOnClickListener(new View.OnClickListener(){ onClick();})
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    
                }

            }
            
         }
}

//动态代理其实就是实现了InvocationHandler接口,并实现里面的invoke方法来实现的
public class ListenerInvocationHandler implements InvocationHandler {
    private Object target;
    private HashMap<String, Method> methodMethod = new HashMap();

    public ListenerInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (target != null) {
            //获取需要拦截的方法
            String methodName = method.getName();
            Method targetMethod = methodMethod.get(methodName);
            if (targetMethod != null) {
                return targetMethod.invoke(target, args);
            }
        }
        return null;
    }

    /**
     * @param methodName 方法名称
     * @param method     需要添加的方法
     */
    public void addMethod(String methodName, Method method) {
        methodMethod.put(methodName, method);
    }
}

最后写法也是非常的简单

首先在onCreate方法里面执行上面方法

    @OnClick({R.id.btn, R.id.tv})
    public void onClick(View v) {
        Toast.makeText(MainActivity.this, "R.id.btn", Toast.LENGTH_SHORT).show();
    }

然后就没有然后

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值