今天模拟一个使用注解来实现findViewById的小demo, ps: 其实只能算是笔记
注解Annotation
* Java中提供,java代码中用来描述,关联任意信息和数据的一种方法(或规则)
* 分类
* 按平台
* JDK自带
* @Override,表示覆写了父类方法
* @Deprecation,表示过时了
* @suppressWarning,忽视警告,suppress,镇压
* 第三方平台
* Android,@Nullable,可以为空的...
* 自定义
* 按时机
* RetentionPolicy.SOURCE,只在源代码显示,变异的时候丢弃
* RetentionPolicy.CLASS,编译时会记录到字节码对象中,执行的时候丢弃
* RetentionPolicy.RUNTIME,运行的时候存在,可以通过反射获取
* 注解的属性的数据类型
* 八大基本数据类型,byte,short,char,int,long,float,double,boolean
* 合法类型,String,Class,Annotation,Enumeration
* 注解可以使用的范围
* 接口,类:ElementType.TYPE
* 方法:ElemengType.METHOD
* 字段:ElementType.FIELD
* 局部变量:ElementType.LOCAL_VARIABLE
* 方法声明的参数:ElementType.PARAMETER
* 构造方法:ElementType.CONSTRUCTOR
* 包声明:ElementType.PACKAGE
思路
* 在Activity中声明View的成员变量
* 给View类型的成员变量添加注解
* 在onCreate方法的setContentView后运行反射解析注解的方法;
* 确保在使用控件前必须获取到对象
* 在inject方法中反射获取到acticity的对象,通过注解的值给activity的字段赋值
代码
* 定义一个注解类型:
package com.kas.utils;
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 ViewId {
int value();//表示值是int类型
}
* Activity中添加注解
public class MainActivity extends Activity {
@ViewId(R.id.bt)
private Button button;
@ViewId(R.id.tv)
private TextView tView;
private int num;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InjectUtils.inject(this); //--->必须在setContentView后调用这个方法
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "按钮被点击了", 0).show();
tView.setText("按钮被点击了哦");
}
});
}
}
* InjectUtils.inject(Activity activity)
public class InjectUtils {
public static void inject(Activity activity){//定义为静态方法,可以直接类名.调用
Class clazz = activity.getClass(); //先获取activity的字节码对象
Class mViewClass = View.class; //获取View的字节码对象,用来判断我们获取的成员变量是不是View对象
Class type = null; //定义一个Class类型的type引用,用来表示获取到的字段的类型
Field[] fields = clazz.getDeclaredFields(); //获取activity对象中的所有字段的引用
for (Field field : fields) { //遍历数组
/**
* 1.设置字段为可以访问
* 如果不设置,在获取私有字段的时候,就会抛出异常
* java.lang.IllegalAccessException: access to field not allowed
*/
field.setAccessible(true); //设置字段为可以访问(ps,如果不设置,而该字段对象是private,就会导致抛异常)
//2.获取字段的类型
type = field.getType(); //获取字段的字节码对象
/**
* 3.判断该字段是否为View的子类
* 返回值为true,表示mViewClass与type表示的类和接口相同,或者mViewClass是type的父类
*/
if (!mViewClass.isAssignableFrom(type)) { //判断该字段的类型是不是View或是View的子类
System.out.println(field + "不是View的子类");
continue;
}
/**
* 判断该字段是否有注解,flag为true表示添加了注解,false表示没有添加注解
* present,提出,介绍,呈现
*/
boolean flag = field.isAnnotationPresent(ViewId.class);//判断字段是否有ViewId的注解
if (!flag) {
continue;
}
/**
* 获取注解对象,如果注解对象为null,则结束本次循环
*/
ViewId viewId = field.getAnnotation(ViewId.class); //获取field对应的注解对象
if (viewId == null) {
continue;
}
/**
* 获取注解表示的id
*/
int valueId = viewId.value(); //获取注解对象的值
try {
View view = activity.findViewById(valueId); //拿到值后,调用activity中的findViewById,获取View对象
//如果findViewById查找的结果是null,说明id不存在或用户恶意攻击,直接甩出一个异常
if (view == null) {
throw new IllegalStateException("there is a null result when findViewById for " + field);
}
field.set(activity, /*type.cast(view)*/view);//设置字段field的值, type.cast(view),表示将view强转为type类型
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
小结
* Annotation
* 定义注解:
public @interface ViewId{
int value();
String desc();
}
* 给注解声明值
- 注解有多个属性的时候
@ViewId(desc = "button",value = R.id.bt)
private Button button;
//值随便填
* 反射获取值
boolean flag = field.isAnnotationPresent(ViewId.class);
ViewId viewId = field.getAnnotation(ViewId.class);
int valueId = viewId.value(); //方法与定义注解的时候相互对应
String desc = viewId.desc()
* Field,
* boolean getAnnotationParent(Class annotation)
* 判断this字段是否包含了annotation注解
* Annotation getAnnotationParent(Class annotation)
* 获取制定的annotation注解对象
* int annotation.value();//获取注解对象对应的值
* Class<?> getType()
* 获取this字段的字节码对象
* void setAccessible(boolean acc)
* 设置为可以访问的,如果获取到的是私有的成员变量,如果不提前设置就操作的话,就会抛出异常 IllegalAccessException: access to field not allowed
* 访问出错,不允许访问
* Class
* boolean isAssignableFrom(Class other)
* 判断this是不是other类型或是other的子类
* assignable,可分配的
* T cast(Object obj),
* 将obj对象强转为T,(this表示的就是T类型的一个对象)