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;
}
}
代码地址: 点击打开链接