安卓进阶之自己实现 ViewInject框架

以前做web开发的时候经常用得到的就是SSH框架,即struts2、spring、hibernate三大框架,他们分别负责了不同的层的业务逻辑,其中spring框架是我觉得最猛的一个框架,它几乎贯穿到整个web开发中,而它的特色功能就是IOC、AOP等,AOP是面向切面编程,根据动态代理技术来动态管理我们的java代码,功能非常强大。IOC是控制反转,为什么叫控制反转呢?因为我们一般实例化一个对象都是自己手动通过new来实现,而spring通过自己的IOC技术就可以通过配置或者注解的方法来帮助我们new一个对象,使我们不用关注实例化对象的方法,从而注重程序的业务逻辑,在一方面也能达到很好的解耦作用。
现在安卓中也引入了IOC技术,可以在网上下载到ViewInject项目来使用我们的IOC功能,于是我们又多了一个新技能,使用别人写的ViewInject框架。程序员中有一句话叫做不要重复的制造轮子,我们大多数程序员都是拿来主义,只要功能实现就行,而不去管其中的原理,不自己是考虑一下怎么实现的,但叫自己做的话能不能实现呢?于是我决定动手实现ViewInject框架,虽然比较简陋,但是自己的东西写出来才是硬道理哇。


要实现ViewInject框架需要了解一下java中反射的概念,所谓反射其实就是通过非new的方式来获取我们的属性、方法等内容,同时也可以通过反射来实例化一个对象,功能非常强大,是设计框架的必备良药。通过几个例子来说明:

//有一个A类,我们传统的可以通过new A来获取对象,并且调用里面的方法或者属性
//主要有一个name属性,它是私有的,如果通过new的方法还能调用吗?
class A {
    private int age = 100;
    private String name = "私有属性";

    public void sayHellow() {
        System.out.println("你好");
    }

}
//如果需要调用里面的私有属性或者私有方法,则需要通过法神来实现:
public static void main(String[] args) throws Exception {
        Class clazz = A.class;
        // 实例化一个A对象
        A a = clazz.newInstance();
        // 获取所有的属性(包括私有属性),也可以使用getFields方法,但是这样不能
        // 获取到私有属性
        Field[] fileds = clazz.getDeclaredFields();
        for (Field f : fileds) {
            // 如果需要访问私有属性,这里必须要设置为true
            f.setAccessible(true);
            System.out.println(f.get(a));
        }
        // 获取所有的方法(包括私有方法),也可以使用getMethods方法,但是这样不能
        // 获取到私有方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method m : methods) {
            // 如果需要访问私有方法,这里必须要设置为true
            m.setAccessible(true);
            m.invoke(a, null);
        }
    }
    打印结果:
    100
    私有属性
    你好

可以看到通过反射能够正常访问私有属性,私有方法等,非常厉害!
我们在整合SSH框架的时候有时候为了简化经常使用注解的方式,注解英文名幼教Annotation,非常好用,用法也很简单,这里略过。。。
下面就是实现自己的ViewInject框架了
首先看一下我们使用自己的框架的代码:

@ContentView(R.layout.activity_main)//这里使用了注解
public class MainActivity extends Activity {
    @ViewInject(R.id.tv1)//这里使用了注解
    private TextView tv;
    @ViewInject(R.id.btn1)//这里使用了注解
    private Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ViewUtil.inject(this);//自定义的工具类
        tv.setText("通过自定义矿建实现的");
    }

    @OnClick({ R.id.btn1, R.id.btn2 })//这里使用了注解
    public void btn(View view) {
        switch (view.getId()) {
        case R.id.btn1:
            Toast.makeText(getApplicationContext(), "点击了按钮1",
                    Toast.LENGTH_SHORT).show();
            break;
        case R.id.btn2:
            Toast.makeText(getApplicationContext(), "点击了按钮2",
                    Toast.LENGTH_SHORT).show();
            break;
        }
    }

}

可以看到我们总共有三个注解类型:ContentView、ViewInject、OnClick以及一个ViewUtil工具类
首先定义自己的ContentView注解类型:

//运行环境,此处是运行时
@Retention(RetentionPolicy.RUNTIME)
// 此处表示注解可以存放的位置,此处设置为在类上
@Target(ElementType.TYPE)
public @interface ContentView {
    int value();
}

定义ViewInject类型:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ViewInject {
    int value();
}

定义OnClick类型:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    //此处返回一个数据表示可以通过定义多个
    int[] value();
}

最后使我们的核心控制类:

/**
 * 核心控制类
 * 
 * @author leilu
 * 
 */
public class ViewUtil {

    /**
     * 开始注解
     * 
     * @param activity
     */
    public static void inject(Activity activity) {
        // 判断对应的Activity上面是否使用了ContentView注解
        if (activity.getClass().isAnnotationPresent(ContentView.class)) {
            // 如果加了注解则转换为ContentView注解
            ContentView contentView = activity.getClass().getAnnotation(
                    ContentView.class);
            int layoutResID = contentView.value();// 获取布局的资源id
            activity.setContentView(layoutResID);// 设置布局内容
            findView(activity);// 绑定view
            registEvent(activity);// 注册点击事件
        }
    }

    /**
     * 注册点击事件
     * 
     * @param activity
     */
    private static void registEvent(final Activity activity) {
        // 获取所有的方法
        Method[] method = activity.getClass().getMethods();
        for (final Method m : method) {
            // 判断方法上面使用存在OnClick注解
            if (m.isAnnotationPresent(OnClick.class)) {
                // 如果存在则转换
                OnClick on = m.getAnnotation(OnClick.class);
                int[] values = on.value();// 获取需要绑定点击事件的控件id
                for (int i = 0; i < values.length; i++) {
                    final View view = activity.findViewById(values[i]);
                    // 给其设置点击事件
                    view.setOnClickListener(new OnClickListener() {

                        @Override
                        public void onClick(View v) {
                            try {
                                m.invoke(activity, view);// 调用activity里面的方法
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }
        }
    }

    /**
     * 绑定view控件
     * 
     * @param activity
     */
    private static void findView(Activity activity) {
        // 获取所有属性
        Field[] fields = activity.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            // 判断属性上面是否存在ViewInject注解
            if (field.isAnnotationPresent(ViewInject.class)) {
                ViewInject vi = field.getAnnotation(ViewInject.class);
                int resourceId = vi.value();// 获取控件资源id
                try {
                    field.set(activity, activity.findViewById(resourceId));// 绑定控件
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

OK,自定义框架实现完毕!看看效果:
安卓进阶之自己实现 ViewInject框架_第1张图片

你可能感兴趣的:(安卓进阶学习)