您的位置:首页 > Web前端

关于通过注解反射实现findViewById(int x);的原理

2016-06-14 19:32 507 查看
先从简单的地方开始说起,

package 通过注解反射;

public class Person {
private int age;

private String name;

public int getAge(){
return age;
}

public String getName(){
return name;
}

public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Person p = new Person();
System.out.println(p.getAge());
System.out.println(p.getName());
}
}


看这个简单的类,有点基础的都知道,会输出0和null。
然后,我会通过注解改变age和name的值

添加了注解后的类就是这样:

package 通过注解反射;

public class Person {
@MyAnnotation(number = 20)
private int age;

@MyAnnotation(string = "小明")
private String name;

public int getAge(){
return age;
}

public String getName(){
return name;
}

public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Person p = new Person();
X.init(p);
System.out.println(p.getAge());
System.out.println(p.getName());
}
}

输入结果如下图:



看看本人定义的注解,

package 通过注解反射;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 注解会在class字节码文件中存在,在运行时可以通过反射获取到
*
*/
@Retention(RetentionPolicy.RUNTIME)

/**
*
1.CONSTRUCTOR:用于描述构造器
   2.FIELD:用于描述成员变
   3.LOCAL_VARIABLE:用于描述局部变量
   4.METHOD:用于描述方法
   5.PACKAGE:用于描述包
   6.PARAMETER:用于描述参数
   7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
*/
@Target(ElementType.FIELD)
public @interface MyAnnotation {
int number() default -1;
String string() default "NULL";
}
关于注解的详细说明,请童鞋们另寻资料。
<span style="white-space:pre"> </span>int number() default -1;
String string() default "NULL";在两个default在之后将会用到。请记住,注解只起到辅助功能,不会真正影响到实际代码。

有留意的童鞋都会在main方法留意到这个

X.init(p);这个也是本章重头戏,这个方法是通过反射得到注解的值,再通过反射给成员变量赋值,看看怎么实现。
public static void init(Object object) throws IllegalArgumentException, IllegalAccessException{
Class clazz = object.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
if (annotation == null) {
return;
}
int number = annotation.number();
String string = annotation.string();
field.setAccessible(true);
if (number != -1) {
field.setInt(object, number);
}

if (!string.equals("NULL")) {
field.set(object, string);
}
}
}

Class clazz = object.getClass();
这句话是得到Class 的对象,此时打印clazz.getSimpleName()会打印出 Person  。

clazz.getDeclaredFields();这一句是得到该类(Person.class)的所有成员变量(无论是否私有)
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
if (annotation == null) {
return;
}
得到成员变量上方的注解,如果没有注解则为null,此刻就会返回,不执行下面方法。

int number = annotation.number();
String string = annotation.string();拿到注解定义的2个值,如果成员变量上没有注解,则为默认值。
field.setAccessible(true);
如果是私有成员变量,就要设置才这句话才可以反射。

if (number != -1) {
field.setInt(object, number);
}

if (!string.equals("NULL")) {
field.set(object, string);
}因为每个成员变量只有一个注解,注解上没有用到的值会是默认值,因此会用来判断注解了来个值,然后给成员变量赋值。

这样就实现通过注解给成员变量赋值了。

---------------------------------下面是安卓部分---------------------------------------------------

private static final class ViewHolder {
@ViewInject(R.id.tv_content)
TextView tv_content;
@ViewInject(R.id.vwd)
ViewWithDraged vwd;
@ViewInject(R.id.btn_1)
Button btn1;
@ViewInject(R.id.btn_2)
Button btn2;

public ViewHolder(View view) {
// x.view().inject(this,view);
X.inject(this,view);
}
}有这么一个内部类也是用过注解来绑定id,注释的代码是xUtils的方法,
X.inject(this,view);是博主自己实现的,注解用的也是xUtils的,其实都一样,
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.xutils.view.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

int parentId() default 0;
}


看看具体的实现方法:

public static void inject(Object object, View view) {
Class obejctClass = object.getClass();
Class viewClass = view.getClass();
// 获取object的所有成员变量
Field[] fields = obejctClass.getDeclaredFields();
try {
// 通过反射拿到View的findViewById
Method findViewById = viewClass.getMethod("findViewById", int.class);
for (Field field : fields) {
ViewInject annotation = field.getAnnotation(ViewInject.class);
if (annotation!=null){
// 拿到View的子类和对应的id值
// Log.d("X", field.getName() + "-----" + annotation.value());
field.setAccessible(true);
// 相当于view.findViewById(R.id.xxx);
Object invoke = findViewById.invoke(view, annotation.value());
// 相当于 TextView tv = (TextView)view.findViewById();
field.set(object,invoke);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
Class obejctClass = object.getClass();
Class viewClass = view.getClass();
是分别得到Object.class 和 View.class的Class 对象。
每个方法的通途都写上注释了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息