1.注解的定义
@Target(ElementType ,TYPE)
@Retention(RetentionPolicy , SOURCE)
public @interface Briana{
String value() default "XXX";
}
元注解:注解上的注解
Target:指明能注解的地方
Retention:注解存活的阶段(SOURCE源码,CLASS字节码,RUNTIME运行时)
2.反射
运行时:反射基于class
3.反射与注解结合
Field:获得自己+父类的成员(不包括private,只能是public)
DecleredFile:只能获得自己的成员(不包括父类,所有的作用域)
4.自定义注解的实现
① 模仿butterknife的@BindView注解实现findViewById。
新建一个注解,把它命名为InjectView。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
@IdRes int value();//添加一个语法检查,只能是id
}
新建一个AnnotationUtils工具类,来实现这个注解。
public class AnnotationUtils {
public static void init(Activity activity) {
InjectView(activity);
}
public static void InjectView(Activity activity) {
Class<? extends Activity> cla = activity.getClass();
Field[] fields = cla.getDeclaredFields();//获取这个类所有的成员
for (Field field : fields) {
//判断属性是否被InjectView注释声明
if (field.isAnnotationPresent(InjectView.class)) {
InjectView injectView = field.getAnnotation(InjectView.class);
int id = injectView.value();//这个value就是传进来的R.id.XXX
View view = activity.findViewById(id);
//反射设置
field.setAccessible(true);//设置访问权限,允许操作private的属性
try {
field.set(activity, view);//反射赋值
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
以上,这个自定义注解就已经完成了,接下来使用这个注解@InjectView试试效果。
在activity_main.xml创建一个id为tv1的TextView,然后在MainActivity里面修改如下:
@InjectView(R.id.tv1)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AnnotationUtils.init(this);
tv.setText("使用注解绑定组件");
}
点击运行,可以看到TextView显示为 “使用注解绑定组件”。
是不是很简单?那么我们来尝试一下点击事件绑定。
② 实现butterknife的@OnClick,实现按钮的点击事件绑定
新建一个注解,命名为@OnClick。
@Target(ElementType.METHOD)//与@InjectView不一样,这个注解是注释在方法上的
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
@IdRes int value();
}
接下来,继续在AnnotationUtils类中编写方法来实现@OnClick注解。
public class AnnotationUtils {
public static void init(Activity activity) {
InjectView(activity);
OnClick(activity);//别忘了调用这个方法
}
public static void OnClick(final Activity activity) {
Class<? extends Activity> cla = activity.getClass();
Method[] methods = cla.getDeclaredMethods();
for (final Method method : methods) {
if (method.isAnnotationPresent(OnClick.class)) {
OnClick onClick = method.getAnnotation(OnClick.class);
int id = onClick.value();
View view = activity.findViewById(id);
view.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
method.setAccessible(true);
try {
method.invoke(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
}
}
}
以上,@OnClick就完成了。
现在来测试一下,新建一个SecondActivity, 在activity_main.xml新建一个Button。想要实现的功能是,点击button,实现从MainActivity跳转到SeconActivity。
修改MainActivity的代码如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@InjectView(R.id.tv1)
TextView tv;
@InjectView(R.id.button)
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AnnotationUtils.init(this);
tv.setText("使用注解绑定组件");
}
@OnClick(R.id.button)
public void onClick(View view) {
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
}
运行一下,康康效果~
再来个例子吧~
③ 写一个@GetBean注解,实现activity跳转过程中参数的接收。
首先,我们先新建一个User类,通过Intent把user对象从MainActivity传递到SecondActivity中,再在SecondActivity中使用@GetExtra注解来自动获取。
我们让User类实现Parcelable接口。
public class User implements Parcelable {
String name;
int age;
String gender;
public User() {
}
public User(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
protected User(Parcel in) {
name = in.readString();
age = in.readInt();
gender = in.readString();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
parcel.writeInt(age);
parcel.writeString(gender);
}
}
其次,创建一个名为GetBean的注解。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GetExtra {
String value();
}
接下来,在工具类中编写这个注解@GetBean的具体实现。
public class AnnotationUtils {
public static void init(Activity activity) {
InjectView(activity);
OnClick(activity);
GetBean(activity);
}
public static void GetBean(Activity activity) {
Class<? extends Activity> cla = activity.getClass();
Field[] fields = cla.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(GetBean.class)) {
GetBean getExtra = field.getAnnotation(GetBean.class);
String key = getExtra.value();
User user = activity.getIntent().getParcelableExtra(key);
field.setAccessible(true);
try {
field.set(activity, user);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
以上,@GetBean已经写好了,接下来测试一下效果。
在MainActivity.java里面,修改onClick方法如下:
@OnClick(R.id.button)
public void onClick(View view) {
User user = new User("briana",21,"女");
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("user",user);
startActivity(intent);
}
在SecondActivity.java文件里面修改如下:
public class SecondActivity extends AppCompatActivity {
@GetBean("user")
User user;
@InjectView(R.id.tv1)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
AnnotationUtils.init(this);
textView.setText("姓名:"+user.getName()+'\n'+"年龄:"+user.getAge()+'\n'+"性别:"+user.getGender());
}
}
看看效果: