在android开发中,进行界面渲染的时候,必须先对各种View进行初始化,比如:
<span style="font-size:18px;">TextView t = (TextView) findViewById(R.id.x);</span>
这是我们最常见的获取xml布局中一个textview对象的过程。如果视图过多的话,就会重复的调用这个方法。这样的话,不仅对开发者来说很烦躁,而且从视觉上看代码显得很臃肿。
有一种解决方法是使用注解的方式来初始化这些视图控件,但是这种方法会牺牲一定的性能,因为它是通过反射的方式实现的,不过在可以接受的范围内。下面就来说说是如何实现的。
首先编写一个注解类,如下:
<span style="font-size:18px;">@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
public int id();
}</span>
@Target(ElementType.FIELD):表示我们需要注解的是一个字段
@Retention(RetentionPolicy.RUNTIME):表示在源码、编译好的.iclass文件中保留信息,在执行的时候会把这些信息加载到JVM中。
然后编写一个处理注解的方法,
我们的目标是根据View的ID取初始化View,所以首先得取到view的ID,很简单,通过注解类的id()方法便可以得到。
但是BindView是不能new的,这时就需要用到Java的反射机制。
我们知道,每一个继承自Object类的类都会继承一个getClass()方法,这个方法返回的是该类的Class对象,同时也是该类的二进制对象。
Class中有一个方法叫getDeclaredFields(),是用来返回这个类的全部字段,返回类型是Field[]
通过Field对象的getAnnotation(Class<?>)方法,我们可以获取到任何一个注解类型的对象,比如BindView
代码如下:
public static void initBindView(Object currentClass, View sourceView) {
// 通过反射获取到全部属性,反射的字段可能是一个类(静态)字段或实例字段
Field[] fields = currentClass.getClass().getDeclaredFields();
if (fields != null && fields.length > 0) {
for (Field field : fields) {
// 返回BindView类型的注解内容
if (field.isAnnotationPresent(BindView.class)) {
BindView bindView = field.getAnnotation(BindView.class);
int viewId = bindView.id();;
try {
field.setAccessible(true);
// 将currentClass的field赋值为sourceView.findViewById(viewId)
field.set(currentClass, sourceView.findViewById(viewId));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
注意,该方法必须在setContentView之后调用。
最后在你的activity或fragment里通过如下的方式
@BindView
(id = R.id.x
)
private
TextView t;
就可以初始化TextView了,而不用再去编写
TextView t = (TextView) findViewById(R.id.x); 这一部分的代码了。