上一篇博客我们已经带大家简单的吹了一下IoC,实现了Activity中View的布局以及控件的注入,如果你不了解,请参考:Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)

本篇博客将带大家实现View的事件的注入。

1、目标效果

上篇博客,我们的事件的代码是这么写的:

光有View的注入能行么,我们写View的目的,很多是用来交互的,得可以点击神马的吧。摒弃传统的神马,setOnClickListener,然后实现匿名类或者别的方式神马的,我们改变为:

package com.zhy.zhy_xutils_test;  

import android.view.View;
import android.widget.Button;
import android.widget.Toast;  

import com.zhy.ioc.view.annotation.ContentView;
import com.zhy.ioc.view.annotation.OnClick;
import com.zhy.ioc.view.annotation.ViewInject;  

@ContentView(value = R.layout.activity_main)
public class MainActivity extends BaseActivity  {
    @ViewInject(R.id.id_btn)
    private Button mBtn1;
    @ViewInject(R.id.id_btn02)
    private Button mBtn2;  

    @OnClick({ R.id.id_btn, R.id.id_btn02 })
    public void clickBtnInvoked(View view)   {
        switch (view.getId())  {
        case R.id.id_btn:
            Toast.makeText(this, "Inject Btn01 !", Toast.LENGTH_SHORT).show();
            break;
        case R.id.id_btn02:
            Toast.makeText(this, "Inject Btn02 !", Toast.LENGTH_SHORT).show();
            break;
        }
    }
}  

直接通过在Activity中的任何一个方法上,添加注解,完成1个或多个控件的事件的注入。这里我把onCreate搬到了BaseActivity中,里面调用了ViewInjectUtils.inject(this);

2、实现

1、注解文件

package com.zhy.ioc.view.annotation;  

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;  

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
    Class<?> listenerType();  

    String listenerSetter();  

    String methodName();
}  

EventBase主要用于给OnClick这类注解上添加注解,毕竟事件很多,并且设置监听器的名称,监听器的类型,调用的方法名都是固定的,对应上面代码的:

listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", methodName = "onClick"

Onclick是用于写在Activity的某个方法上的:

如果你还记得,上篇博客我们的ViewInjectUtils.inject(this);里面已经有了两个方法,本篇多了一个:

public static void inject(Activity activity) {
        injectContentView(activity);
        injectViews(activity);
        injectEvents(activity);
 }  

2、injectEvents

/**
     * 注入所有的事件
     *
     * @param activity
     */
    private static void injectEvents(Activity activity)
    {  

        Class<? extends Activity> clazz = activity.getClass();
        Method[] methods = clazz.getMethods();
        //遍历所有的方法
        for (Method method : methods)
        {
            Annotation[] annotations = method.getAnnotations();
            //拿到方法上的所有的注解
            for (Annotation annotation : annotations)
            {
                Class<? extends Annotation> annotationType = annotation
                        .annotationType();
                //拿到注解上的注解
                EventBase eventBaseAnnotation = annotationType
                        .getAnnotation(EventBase.class);
                //如果设置为EventBase
                if (eventBaseAnnotation != null)
                {
                    //取出设置监听器的名称,监听器的类型,调用的方法名
                    String listenerSetter = eventBaseAnnotation
                            .listenerSetter();
                    Class<?> listenerType = eventBaseAnnotation.listenerType();
                    String methodName = eventBaseAnnotation.methodName();  

                    try
                    {
                        //拿到Onclick注解中的value方法
                        Method aMethod = annotationType
                                .getDeclaredMethod("value");
                        //取出所有的viewId
                        int[] viewIds = (int[]) aMethod
                                .invoke(annotation, null);
                        //通过InvocationHandler设置代理
                        DynamicHandler handler = new DynamicHandler(activity);
                        handler.addMethod(methodName, method);
                        Object listener = Proxy.newProxyInstance(
                                listenerType.getClassLoader(),
                                new Class<?>[] { listenerType }, handler);
                        //遍历所有的View,设置事件
                        for (int viewId : viewIds)
                        {
                            View view = activity.findViewById(viewId);
                            Method setEventListenerMethod = view.getClass()
                                    .getMethod(listenerSetter, listenerType);
                            setEventListenerMethod.invoke(view, listener);
                        }  

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

            }
        }  

    } 

嗯,注释尽可能的详细了,主要就是遍历所有的方法,拿到该方法省的OnClick注解,然后再拿到该注解上的EventBase注解,得到事件监听的需要调用的方法名,类型,和需要调用的方法的名称;通过Proxy和InvocationHandler得到监听器的代理对象,显示设置了方法,最后通过反射设置监听器。

这里有个难点,就是关于DynamicHandler和Proxy的出现,如果不理解没事,后面会详细讲解。

3、DynamicHandler

这里用到了一个类DynamicHandler,就是InvocationHandler的实现类:

package com.zhy.ioc.view;  

import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;  

public class DynamicHandler implements InvocationHandler
{
    private WeakReference<Object> handlerRef;
    private final HashMap<String, Method> methodMap = new HashMap<String, Method>(
            1);  

    public DynamicHandler(Object handler)
    {
        this.handlerRef = new WeakReference<Object>(handler);
    }  

    public void addMethod(String name, Method method)
    {
        methodMap.put(name, method);
    }  

    public Object getHandler()
    {
        return handlerRef.get();
    }  

    public void setHandler(Object handler)
    {
        this.handlerRef = new WeakReference<Object>(handler);
    }  

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        Object handler = handlerRef.get();
        if (handler != null)
        {
            String methodName = method.getName();
            method = methodMap.get(methodName);
            if (method != null)
            {
                return method.invoke(handler, args);
            }
        }
        return null;
    }
}  

好了,代码就这么多,这样我们就实现了,我们事件的注入~~

效果图:

效果图其实没撒好贴的,都一样~~~

3、关于代理

那么,本文结束了么,没有~~~关于以下几行代码,相信大家肯定有困惑,这几行干了什么?

//通过InvocationHandler设置代理
DynamicHandler handler = new DynamicHandler(activity);
handler.addMethod(methodName, method);
Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(),new Class<?>[] { listenerType }, handler);  

InvocationHandler和Proxy成对出现,相信大家如果对Java比较熟悉,肯定会想到Java的动态代理~~~

关于InvocationHandler和Proxy的文章,大家可以参考:http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/ ps:IBM的技术文章还是相当不错的,毕竟有人审核还有奖金~

但是我们的实现有一定的区别,我为什么说大家疑惑呢,比如反射实现:

mBtn2.setOnClickListener(this);这样的代码,难点在哪呢?

1、mBtn2的获取?so easy

2、调用setOnClickListener ? so easy

but , 这个 this,这个this是OnClickListener的实现类的实例,OnClickListener是个接口~~你的实现类怎么整,听说过反射newInstance对象的,但是你现在是接口!

是吧~现在应该明白上述几行代码做了什么了?实现了接口的一个代理对象,然后在代理类的invoke中,对接口的调用方法进行处理。

4、代码是最好的老师

光说谁都理解不了,你在这xx什么呢??下面看代码,我们模拟实现这样一个情景:

Main类中实现一个Button,Button有两个方法,一个setOnClickListener和onClick,当调用Button的onClick时,触发的事件是Main类中的click方法

涉及到4个类:

Button

package com.zhy.invocationhandler;  

public class Button
{
    private OnClickListener listener;  

    public void setOnClickLisntener(OnClickListener listener)
    {  

        this.listener = listener;
    }  

    public void click()
    {
        if (listener != null)
        {
            listener.onClick();
        }
    }
}  

OnClickListener接口

package com.zhy.invocationhandler;  

public interface OnClickListener
{
    void onClick();
}  

OnClickListenerHandler , InvocationHandler的实现类

package com.zhy.invocationhandler;  

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;  

public class OnClickListenerHandler implements InvocationHandler
{
    private Object targetObject;  

    public OnClickListenerHandler(Object object)
    {
        this.targetObject = object;
    }  

    private Map<String, Method> methods = new HashMap<String, Method>();  

    public void addMethod(String methodName, Method method)
    {
        methods.put(methodName, method);
    }  

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {  

        String methodName = method.getName();
        Method realMethod = methods.get(methodName);
        return realMethod.invoke(targetObject, args);
    }  

}  

我们的Main

package com.zhy.invocationhandler;  

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;  

public class Main
{
    private Button button = new Button();  

    public Main() throws SecurityException, IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException
    {
        init();
    }  

    public void click()
    {
        System.out.println("Button clicked!");
    }  

    public void init() throws SecurityException,
            NoSuchMethodException, IllegalArgumentException,
            IllegalAccessException, InvocationTargetException
    {
        OnClickListenerHandler h = new OnClickListenerHandler(this);
        Method method = Main.class.getMethod("click", null);
        h.addMethod("onClick", method);
        Object clickProxy = Proxy.newProxyInstance(
                OnClickListener.class.getClassLoader(),
                new Class<?>[] { OnClickListener.class }, h);
        Method clickMethod = button.getClass().getMethod("setOnClickLisntener",
                OnClickListener.class);
        clickMethod.invoke(button, clickProxy);  

    }  

    public static void main(String[] args) throws SecurityException,
            IllegalArgumentException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException
    {  

        Main main = new Main();  

        main.button.click();
    }  

}  

我们模拟按钮点击:调用main.button.click(),实际执行的却是Main的click方法。

看init中,我们首先初始化了一个OnClickListenerHandler,把Main的当前实例传入,然后拿到Main的click方法,添加到OnClickListenerHandler中的Map中。

然后通过Proxy.newProxyInstance拿到OnClickListener这个接口的一个代理,这样执行这个接口的所有的方法,都会去调用OnClickListenerHandler的invoke方法。

但是呢?OnClickListener毕竟是个接口,也没有方法体~~那咋办呢?这时候就到我们OnClickListenerHandler中的Map中大展伸手了:

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{

String methodName = method.getName();
Method realMethod = methods.get(methodName);
return realMethod.invoke(targetObject, args);
}

我们显示的把要执行的方法,通过键值对存到Map里面了,等调用到invoke的时候,其实是通过传入的方法名,得到Map中存储的方法,然后调用我们预设的方法~。

这样,大家应该明白了,其实就是通过Proxy得到接口的一个代理,然后在InvocationHandler中使用一个Map预先设置方法,从而实现Button的onClick,和Main的click关联上。

现在看我们InjectEvents中的代码:

//通过InvocationHandler设置代理
DynamicHandler handler = new DynamicHandler(activity);
//往map添加方法
handler.addMethod(methodName, method);
Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class<?>[] { listenerType }, handler); 

是不是和我们init中的类似~~

好了,关于如何把接口的回调和我们Activity里面的方法关联上我们也解释完了~~~

注:部分代码参考了xUtils这个框架,毕竟想很完善的实现一个完整的注入不是一两篇博客就可以搞定,但是核心和骨架已经实现了~~大家有兴趣的可以继续去完善~

源码点击下载

---------------------------------------------------------------------------------------------------------------------------------------

最后贴个广告:

第一次录制视频~~~还望大家支持,共同进步~

高仿微信5.2.1主界面及消息提醒

Android 进阶 Android 中的 IOC 框架 【ViewInject】 (下)的更多相关文章

  1. Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (下)

    上一篇博客我们已经带大家简单的吹了一下IoC,实现了Activity中View的布局以及控件的注入,如果你不了解,请参考:Android 进阶 教你打造 Android 中的 IOC 框架 [View ...

  2. Android 进阶Android 中的 IOC 框架 【ViewInject】 (上)

    1.概述 首先我们来吹吹牛,什么叫IoC,控制反转(Inversion of Control,英文缩写为IoC),什么意思呢? 就是你一个类里面需要用到很多个成员变量,传统的写法,你要用这些成员变量, ...

  3. Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39269193,本文出自:[张鸿洋的博客] 1.概述 首先我们来吹吹牛,什么叫Io ...

  4. Android Butterknife使用方法总结 IOC框架

    前言: ButterKnife是一个专注于Android系统的View注入框架,以前总是要写很多findViewById来找到View对象,有了ButterKnife可以很轻松的省去这些步骤.是大神J ...

  5. Android中的IOC框架,完全注解方式就可以进行UI绑定和事件绑定

    转载请注明出处:http://blog.csdn.net/blog_wang/article/details/38468547 相信很多使用过Afinal和Xutils的朋友会发现框架中自带View控 ...

  6. 【转】Android中的IOC框架,完全注解方式就可以进行UI绑定和事件绑定

    转载请注明出处:http://blog.csdn.net/blog_wang/article/details/38468547 相信很多使用过Afinal和Xutils的朋友会发现框架中自带View控 ...

  7. Android v4包中的 SwipeRefreshLayout 官方的下拉刷新组件

    SwipeRefreshLayout在v4包下,相应的v4Demo中也有相应的样例.假设没有请下载最新support-v4 SwipeRefreshLayout 仅仅能有一个直接子View,可能是一个 ...

  8. Android进阶——Android视图工作机制之measure、layout、draw

    自定义View一直是初学者们最头疼的事情,因为他们并没有了解到真正的实现原理就开始试着做自定义View,碰到很多看不懂的代码只能选择回避,做多了会觉得很没自信.其实只要了解了View的工作机制后,会发 ...

  9. Android进阶——Android消息机制之Looper、Handler、MessageQueen

    Android消息机制可以说是我们Android工程师面试题中的必考题,弄懂它的原理是我们避不开的任务,所以长痛不如短痛,花点时间干掉他,废话不多说,开车啦 在安卓开发中,常常会遇到获取数据后更新UI ...

随机推荐

  1. jQuery 插件autocomplete

    jQuery 插件autocomplete 自动加载 参考: http://www.cnblogs.com/Peter-Zhang/archive/2011/10/22/2221147.html ht ...

  2. php对二维数组进行相关操作(排序、转换、去空白等)

    php对二维数组进行相关操作(排序.转换.去空白等) 投稿:lijiao 字体:[增加 减小] 类型:转载 时间:2015-11-04   这篇文章主要介绍了php对二维数组进行相关操作,包括php对 ...

  3. Python 实现粒子滤波

    #转# -*- coding=utf-8 -*-# 直接运行代码可以看到跟踪效果# 红色的小点代表粒子位置# 蓝色的大点表示跟踪的结果# 白色的方框表示要跟踪的目标# 看懂下面两个函数即可from n ...

  4. 【后台测试】Linux下小试jmeter

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处.  转载请注明出处:http://www.cnblogs.com/by-dream/p/5784288.html 前言 上一篇主要讲了在Window ...

  5. inittab 分析

    内核初始化后,启动init进程/sbin/init,读取/etc/inittab文件进行初始化. 参考链接 http://wenku.baidu.com/view/5a82b5f67c1cfad619 ...

  6. [转]C++11 多线程

    转载自:http://www.cnblogs.com/zhuyp1015/archive/2012/04/08/2438288.html C++11开始支持多线程编程,之前多线程编程都需要系统的支持, ...

  7. SQL server2012怎么备份数据库(设置自动备份)

    1.打开SQL server配置管理器,设置sql server服务里的SQL server代理服务为自动并启动 2.启动Master Data Services Configuration Mana ...

  8. python_os

    1. 基本功能的介绍 os模块包含普通的操作系统的功能 2. 常用的变量 (1)os.name 获取正在使用的平台, Windows 返回 nt, Linux或者Unix 返回 posix 3. 常用 ...

  9. 使用Karma 来进行 JavaScript 测试

    最近接触了一些新的前端开发知识,主要是利用AngularJS做single page application.我也借这个机会,花了几天时间了解了如何对javascript进行测试. 这里将介绍一些使用 ...

  10. AngularJS Best Practices: ngRoute

    app/----- components/---------- users/--------------- controllers/-------------------- users.control ...