使用Butterknife等视图注入工具可以减少findViewById这样没有营养的代码,那么这类工具的原理是什么呢?反射。通过反射,我们可以获取一个类的成员变量、方法,并进行修改和利用。那么我们就动手来写一个InjetView工具吧。
首先,创建一个注解类,用来标识要注解的变量。
@Target(ElementType.FIELD)//表示该注解作用于成员变量等
@Retention(RetentionPolicy.RUNTIME)//表示该注解在运行时保留。
public @interface InjetView {
int value();
}
@Target、@Retention是元注解,就是不能在分割解释的注解(有点类似单位“个”,比如我们描述苹果可以说一个苹果,两个苹果,但是怎么来描述个呢?个是单位,O(∩_∩)O哈哈~),前者标识作用对象,后者标识作用生命期。
接下来我们创建该注解的处理器,总的思想就是根据传入Context的字节码,获取该字节码中含有InjetView注解的成员变量,通过反射调用findViewById()方法,获取到View,并赋予该字段。
public class InjetViewUtil {
public static void injet(Context context) throws NoSuchMethodException {
Class<? extends Context> aClass = context.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
if (declaredFields != null) {
Method findViewById = aClass.getDeclaredMethod("findViewById", Integer.TYPE);
Object view;
int id;
InjetView annotation;
for (int i = 0; i < declaredFields.length; i++) {
annotation = declaredFields[i].getAnnotation(InjetView.class);
if (annotation != null) {
id = annotation.value();
try {
declaredFields[i].setAccessible(true);
view = findViewById.invoke(context, id);
declaredFields[i].set(context, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
}
大致上就是这样来实现View注入、bind,Butterknife还有@OnClick注解实现点击事件的处理等,等闲下来详细的分析下。