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

本文介绍如何使用Java反射机制结合自定义注解为类成员变量动态赋值,并展示了一个基于注解的视图绑定实现。

先从简单的地方开始说起,

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 对象。

每个方法的通途都写上注释了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值