XUtils 简单实现

来源:互联网 发布:ubuntu安装libapu 编辑:程序博客网 时间:2024/06/05 07:05

XUtils是一个很强大的框架,几乎涵盖了我们一个App常用的功能,它包含四部分

一.DBUtils:数据库操作

二.ViewUtils:通过注解的方式进行UI绑定,资源和事件绑定

三.HttpUtils:网络连接,同步异步,上传下载 都涵盖

四.BitmapUtils:加载本地/网络图片,解决了滑动时图片错位的问题,以及图片的内存管理

git项目地址:https://github.com/wyouflf/xUtils


今天这篇文章主要是为了记录下,xUtils中ViewUtils的实现思路。当然原框架是很强大的,这里只是简单实现,希望能给学习XUtils的人一些帮助

ViewUtils进行布局的绑定和资源的初始化,事件的绑定主要是通过注解+反射的方式实现的,主要的类如下(图画的丑见谅)





简单说下java的元注解,它的作用主要是注解其他注解,java5.0定义了4个meta-annotation类型,他们分别是:

@Type:说明了Annotation所修饰对象的范围

@Retention:定义了Annotation保留的时长

@Document:可以被文档化,是一个标记注解,没有成员

@Inherited:标记注解,被该注解修饰的class的子类也继承该注解

@Type:

   作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
    

           取值(ElementType)有:
      1.CONSTRUCTOR:用于描述构造器
     2.FIELD:用于描述域
     3.LOCAL_VARIABLE:用于描述局部变量
     4.METHOD:用于描述方法
     5.PACKAGE:用于描述包
     6.PARAMETER:用于描述参数
         7.TYPE:用于描述类、接口(包括注解类型) 或enum声明


@Retention:

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

   取值(RetentionPoicy)有:

     1.SOURCE:在源文件中有效(即源文件保留)
        2.CLASS:在class文件中有效(即class保留)
        3.RUNTIME:在运行时有效(即运行时保


(ps:ViewXutils使用的运行时的注解,所以它和knife比起来是有一定性能损耗的)


绑定布局:

  1.首先定义一个接受Layout Id的Annotation

  2.通过反射的方式得到setContentView 方法invoke一下即可

 1.

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

2.

    public static void injectLayout(Object context) {        //获取注解        //设置        Class<?> clazz = context.getClass();               ContentView hConextView = clazz.getAnnotation(ContentView.class);//获取ContextView注解        if (hConextView == null)            return;        //获取方法        try {            Method setContentView = clazz.getMethod("setContentView", int.class);            int layoutId = hConextView.value();            setContentView.invoke(context, layoutId);        } catch (NoSuchMethodException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (InvocationTargetException e) {            e.printStackTrace();        }    }


初始化控件:

 1.创建一个接受控件id的Annotation

 2.获取注解 ,通过反射得到findViewById方法invoke

 3.将实例化的结果绑定在控价上

1.

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

2,3

  public static void injectView(Object context) {        //1.获取有注解的字段        //2.初始化        //3.绑定        try {            Class<?> clazz = context.getClass();//            Field[] fields = clazz.getFields();//获取所有公共的字段            Field[] fields = clazz.getDeclaredFields();//获取所有申明的字段            Method findViewById = clazz.getMethod("findViewById", int.class);            for (Field field : fields) {                //拿到字段的注解                ViewInject viewInject = field.getAnnotation(ViewInject.class);                if (viewInject == null) {                    continue;                }                int viewId = viewInject.value();                View view = (View) findViewById.invoke(context, viewId);                //绑定                field.setAccessible(true);//设置可写                field.set(context, view);            }        } catch (NoSuchMethodException e) {            e.printStackTrace();        } catch (InvocationTargetException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }    }

绑定事件:

由于安卓的绑定事件很多,我们不可能在工具类中讲每个事件都进行判断,所以我们需要定义一个包含事件的三要素的Base-Annotation,然后再定义各个具体事件的Annotation。在工具类中通过获取Base-Annotation的事件三要素进行操作

1.定义事件 Base-Annotaion

2.定义具体事件Annotation

3.获取Base-Annotation,反射出相应的控件和事件进行调用

1.

@Target(ElementType.ANNOTATION_TYPE)//注解的注解@Retention(RetentionPolicy.RUNTIME)public @interface EventBase {    String listenerSetter();    Class<?> listenerType();    String callbackMothod();}

2.

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, callbackMothod = "onClick")public @interface OnClick {    int[] value() default  -1;}

3.

 public static void injectEvent(Object context) {        //1.获取方法        //2.获取注解        //3.获取注解的注解        //4.根据注解设置相应的事件        Class<?> classzz = context.getClass();        //1        Method[] methods = classzz.getDeclaredMethods();        for (Method method : methods) {            //2            Annotation[] annotations = method.getAnnotations();            for (Annotation annotation : annotations) {                               //3//                EventBase eventBase = annotation.getClass().getAnnotation(EventBase.class);//错误                Class<?> annotationsType = annotation.annotationType();                EventBase eventBase = annotationsType.getAnnotation(EventBase.class);                if (eventBase == null) {                    continue;                }                //获取三要素                String listenerSetter = eventBase.listenerSetter();                Class<?> listenerType = eventBase.listenerType();                String callBack = eventBase.callbackMothod();                //获取控件                //因为我不知道传入的是什么类型的注解,所以通过反射获取到里面的方法                try {                    Method value = annotation.getClass().getMethod("value");                    int[] viewId = (int[]) value.invoke(annotation);                    Method findViewById = classzz.getMethod("findViewById", int.class);                    for (int id : viewId) {                        View view = (View) findViewById.invoke(context, id);                        if(view==null)                            continue;                        //找到需要设置的方法                        Method setListenerMethod = view.getClass().getMethod(listenerSetter, listenerType);                        //将这个注解代表的方法设置给这个控件                        ListenerInvocation invocation = new ListenerInvocation(context, method);                        Object listenerTypeInstance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class<?>[]{listenerType}, invocation);                        //设置方法                        setListenerMethod.invoke(view, listenerTypeInstance);                    }                } catch (NoSuchMethodException e) {                    e.printStackTrace();                } catch (IllegalAccessException e) {                    e.printStackTrace();                } catch (InvocationTargetException e) {                    e.printStackTrace();                }            }        }    }

ListenerInvocation:
public class ListenerInvocation implements InvocationHandler {    private Object holdObject;//方法的持有对象    private Method method;//调用方法    public ListenerInvocation(Object holdObject, Method method) {        this.holdObject = holdObject;        this.method = method;    }    @Override    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {        return this.method.invoke(holdObject, objects);//        return null;    }}


代码地址:点击打开链接