Android注解解析,注解用法,仿xUtils用注解初始化控件、点击事件(二)

这篇相对于之前的有点难度,需要大家熟练Java反射和Proxy代理流程才能看的容易些,因为这两块不是这篇文章的重点,不懂的可以网上查一下,了解个大概,就可以了。

首先,我先贴出MainActivity的点击事件代码,因为比较简单:

 @IOnClick({R.id.btn_login, R.id.btn_login})
    public void showToast(View view) {
        switch (view.getId()) {
            case R.id.btn_login:
                Toast.makeText(this, "登录", Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn_logoff:
                Toast.makeText(this, "注销", Toast.LENGTH_SHORT).show();
                break;
            default:
                break;
        }
    }

不多说,就是一个方法而已,当然,@IOnlick我们还没有写,再看@IOnlick:

//btn_login.setOnClickListener(new View.OnClickListener() {  //setListener listenerType
//  @Override
//  public void onClick(View v) {         // listenerCallback
//  }
//});
@Target(ElementType.METHOD)   //注解应用于其他注解上
@Retention(RetentionPolicy.RUNTIME)
//这个注解需要注意,对应的参数在上面注解的 btn_login 点击事件里对应找即可
@BaseEvent(setListener = "setOnClickListener",//setOnClickListener为View.setOnClickListener
        listenerType = View.OnClickListener.class,//监听的类型为点击事件
        listenerCallback = "onClick")//这个onClick回调,即为setOnClickListener后回调的onClick
public @interface IOnClick {
    int[] value();//因为一个方法可能与多个控件绑定
}


上面有足够的注解,我就不多说了,大概的流程就是先获取到@IOnclick,再通过@IOnclick获取上面的@BaseEvent注解,下面看@BaseEvent:


@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface BaseEvent {

    String setListener();//设置监听方法名

    Class listenerType();//监听类型

    String listenerCallback();//监听回调方法名

}

这个相对简单,上面也都有注解,看一下就懂了,接下来是最关键的,我先讲一下流程,正常的设置点击监听事件的流程如下图(电脑上没有多少软件,临时那PS滑的,大家凑合看吧):

上面的图很简单,我们都能看懂,而我们现在要做的就是改变一下,见下图:


上面的图看明白后,接下来就很简单了没错,我们需要做的就是把onClick方法拦截下来,然后执行我们自己的方法。

贴InjectUtils里最后一个方法的代码  OnClick:

 private static void OnClick(Activity activity) {
//        获取MainActivity
        Class<? extends Activity> clazz = activity.getClass();

//        获取MainActivity中所有方法
        Method[] methods = clazz.getDeclaredMethods();

        for (Method method : methods) {

//            获取方法上对应的@IOnclick的注解
            Annotation[] annotations = method.getAnnotations();

            for (Annotation annotation : annotations) {

//                通过annotationType获取注解@BaseEvent
                Class<? extends Annotation> annotationType = annotation.annotationType();

                //需要判断是否为null
                if (annotationType != null) {

//                    获取@IOnclick注解上的BaseEvent注解
                    BaseEvent baseEvent = annotationType.getAnnotation(BaseEvent.class);

                    //需要判断是否为null,因为有的注解没有@BaseEvent
                    if (baseEvent != null) {

//                        获取@BaseEvent的三个value
                        String callback = baseEvent.listenerCallback();
                        Class type = baseEvent.listenerType();
                        String setListener = baseEvent.setListener();

                        try {

//                            通过反射获取方法,@IOnclick里的int[] value()不需要传参,所以参数省略
                            Method declaredMethod = annotationType.getDeclaredMethod("value");

//                            调用方法,获取到@IOnclick的value,即两个button的id,参数省略
                            int[] valuesIds = (int[]) declaredMethod.invoke(annotation);

//                            这个类稍后会给出代码,目的是拦截方法
                            InjectInvocationHandler handler = new InjectInvocationHandler(activity);
//                            添加到拦截列表
                            handler.add(callback, method);

//                            得到监听的代理对象
                            Proxy proxy = (Proxy) Proxy.newProxyInstance(type.getClassLoader(),
                                    new Class[]{type}, handler);

//                            遍历所有button的id
                            for (int valuesId : valuesIds) {
                                View view = activity.findViewById(valuesId);
//                                通过反射获取方法
                                Method listener = view.getClass().getMethod(setListener, type);
//                                执行方法
                                listener.invoke(view, proxy);
                            }
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }


注解已经很详细,拦截的地方也已经标明,只要原理明白了,理解上去不难,接下来是InjectInvocationHandler的代码:

public class InjectInvocationHandler implements InvocationHandler {

    //    拦截的方法名列表
    private Map<String, Method> map = new HashMap<>();
    //    在这里实际上是MainActivity
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (target != null) {
//            获取方法名
            String name = method.getName();
            Method m = map.get(name);
            if (m != null) {//如果不存在与拦截列表,就执行
                return m.invoke(target, args);
            }
        }
        return null;
    }

    /**
     * 向拦截列表里添加拦截的方法
     */
    public void add(String name, Method method) {
        map.put(name, method);
    }
}


OK,相信这些代码对大家来说更简单,注解很全,接下来我们运行程序,并点击登录按钮:



下面弹出了吐司,证明代码已经生效!喜欢的朋友们留个言吧~~~~~~~~~



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值