- 最近在做复习整理,顺便把整理的一些东西记录下来。
- Annotation(注解)是JDK1.5及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执基本编译时检查。几乎所有的框架设计中都会用到注解类。
- 先看一下一个注解类的格式
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoodlesClick {
int value() default 1;
}
- Target 表示这个注解可以放在那里使用,ElementType枚举常用有 PACKAGE、TYPE、METHOD、FIELD 几种,分别表示可以用在 包,类,方法,和成员变量上面。我们最常见的注解@Override就是用在方法上的。
- Retention 表示这个注解可以保留到什么时候。RetentionPolicy枚举有SOURCE、CLASS、RUNTIME分别表示,在保留在源.java文件,javac后的.class文件,和保留到程序运行时。这篇写的就是要保存在运行时的。
- 注解的声明用@interface声明。
先创建注解类
今天只实现View的绑定和点击事件。
- 先创建两个注解类
//需要导入的包
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
- 这个是点击的注解类NoodlesClick 只能用在方法上。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoodlesClick {
int value() default 1;
}
- 这个是View绑定的注解类NoodlesInject 只能用在成员变量上。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NoodlesInject {
int value() default 1;
}
- 下面是常规的布局文件 和MainActivity的创建
- 布局文件,只有一个TextView和Button
- 这是MainActivity的代码
public class MainActivity extends Activity {
/**
* 这里引用注解 @NoodlesInject
* 后面括号里面的value 为 刚才注解里面的方法的返回值,方法默认值为1;
* int value() default 1;
* 这里将TextView的id传进去。
*/
@NoodlesInject(value = R.id.tv)
TextView mTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 这里是重点。将当前对象传进去,进行View的绑定以及事件的绑定
*/
NoodlesUtil.inject(this);
}
/**
* 这里引用注解 @NoodlesClick
* 后面括号里面的value 为 刚才注解里面的方法的返回值,方法默认值为1;
* int value() default 1;
* 这里将Button的id传进去。
*/
@NoodlesClick(value = R.id.btn)
private void onClick(View v) {
Toast.makeText(this, mTv.getText().toString().trim(), Toast.LENGTH_SHORT).show();
}
}
- NoodlesUtil是用来做具体的注解与Activity的操作的。
public class NoodlesUtil {
/**
*
* DESC : 静态方法,用于给activity层调用 . <br/>
* @param activity 要解析此activity中的成员变量和方法。
*/
public static void inject(Activity activity) {
bindView(activity);
bindClick(activity);
}
/**
*
* DESC : 解析成员变量 . <br/>
* @param activity
*/
private static void bindClick(Activity activity) {
try {
//获取activity的class。
Class<? extends Activity> clazz = activity.getClass();
//利用反射 获取activity中的所有成员变量
Field[] fields = clazz.getDeclaredFields();
//遍历成员变量
for (Field field : fields) {
/**
* getAnnotation 方法为获取成员变量field上是否有NoodlesInject注解。
* @NoodlesInject(value = R.id.tv)
* TextView mTv;
* 这是Activity中写的成员变量,如果返回不为null,则表明上面有此注解
*/
NoodlesInject annotation = field.getAnnotation(NoodlesInject.class);
if (annotation != null) {
//调用注解中的方法,value 获取到值 R.id.tv
int id = annotation.value();
//通过id找View,将本来需要在activity中写的代码放在了这里来。
View view = activity.findViewById(id);
field.setAccessible(true);
//将找到的View赋值给 textView 类似于 TextView mTv = (TextView)view;
field.set(activity, view);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*
* DESC : 解析绑定了点击事件的注解类 . <br/>
* @param activity
*/
private static void bindView(final Activity activity) {
try {
//获取activity的class。
Class<? extends Activity> clazz = activity.getClass();
//利用反射 获取activity中的所有的方法
Method[] methods = clazz.getDeclaredMethods();
//遍历方法
for (final Method method : methods) {
/**
* getAnnotation 方法为获取方法method上是否有NoodlesClick注解。
* @NoodlesClick(value = R.id.btn)
* private void onClick(View v) { ... }
* 这是Activity中写的方法,如果返回不为null,则表明上面有此注解
*/
NoodlesClick annotation = method.getAnnotation(NoodlesClick.class);
if (annotation != null) {
//调用注解中的方法,value 获取到值 R.id.btn
int id = annotation.value();
//通过id找View。
final View view = activity.findViewById(id);
//设置view的点击事件。
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
method.setAccessible(true);
//将View传入method方法,并执行method方法
// --》 就是执行Activity中的onclick方法。
method.invoke(activity, view);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
具体的执行,已经在代码的注释中,这个demo中除了布局文件之外的代码已经全部贴在这里了,运行时注意导包。
xutils3.0 和butterknife 现在已经全部将运行时改为编译时了,至于编译时的原理,后面会整理出来。