XUtils 简单实现

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

一.DBUtils:数据库操作

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

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

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

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


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

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


扫描二维码关注公众号,回复: 3524150 查看本文章




简单说下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;
    }
}


代码地址: 点击打开链接


猜你喜欢

转载自blog.csdn.net/u013209460/article/details/78683308