今天这个小框架的功能跟xutils的一样,通过注解的方式,让我们不用在使用findviewById,以及new OnClickListener。其实他的实现原理,就都是利用的反射和注解来实现。
这里我们先了解一下注解
元注解:
- @Target
- @Retention
- @Documented
- @Inherited
@Target
Target –目标,把…作为目标
顾名思义,他的作用就是:被描述的注解可以用在什么地方,也就是说该注解的使用范围。
看下例子:
//表示该注解只能用在属性上,如果放在其他地方就会报错
@Target(ElementType.FIELD)
public @interface Test {
}
@Target的取值(ElementType.XXX)
- CONSTRUCTOR:用于描述构造方法
- FIELD:用于描述属性
- LOCAL_VARIABLE:用于描述局部变量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述参数
- TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention
Retention 保留
意思是说,在什么时候得以保留该Annotation,换句好理解话就是说,被描述的注解在什么时间内有效,是在运行时?编译时?还是什么时候。
- SOURCE:在源文件中有效(即源文件保留)
- CLASS:在class文件中有效(即class保留)
- RUNTIME:在运行时有效(即运行时保留)
这个就表示,该注解的使用范围是在方法上,在源文件中才有效。
@Documented
Documented 备有证明文件的
这个注解什么意思呢?就是说用了这个注解,当你使用javadoc工具处理的时候,会把该注解信息也包括在生成的文档中。
@Documented
public @interface Test_Documented {
String doTestDocument();
}
public class TestAnnotations {
public static void main(String arg[]) {
doSomeTestRetention();
doSomeTestDocumented();
}
public void doSomeTestRetention() {
}
@Test_Documented(doTestDocument="Hello document")
public void doSomeTestDocumented() {
}
}
@Inherited
Inherited 遗传的 通过继承得到的
这个注解的意思,注解在类上面时,子类会自动继承此注解,否则的话,子类不会继承此注解,该注解只对类有效,对接口,方法属性均无效。
这里有篇文章说的很详尽:
http://blog.csdn.net/snow_crazy/article/details/39381695
当然写这个框架除了注解之外,还需要反射的知识点,这里只简单说明一下,具体详细的内容以后单开一篇。
反射机制获取类有三种方法
我们以MyTestBean为例
public class MyTestBean {
String name;
int age;
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;
}
}
//第一种方式:
//所有的Java对象都有getClass方法。
MyTestBean textView=new MyTestBean(this);
Class<? extends MyTestBean> aClass = myTestBean.getClass();
//第二种方式
//java中每个对象都有class属性
Class<MyTestBean> aClass = MyTestBean.class;
//第三种方法,通过完整的类名来获取类
Class<?> aClass2 = Class.forName("test.com.duanzi.ui.MyTestBean");
反射机制获取该类的属性和方法
//获取的方法
try {
MyTestBean myTestBean=new MyTestBean();
myTestBean.setName("周杰伦");
Class<? extends MyTestBean> aClass = myTestBean.getClass();
Method getName = aClass.getDeclaredMethod("getName", new Class<?>[]{});
String name = (String) getName.invoke(myTestBean, new Object[]{});
Log.d("AAA",name);
} catch (Exception e) {
e.printStackTrace();
}
//获取该类的属性
try {
MyTestBean myTestBean=new MyTestBean();
myTestBean.setAge(30);
Class<? extends MyTestBean> aClass = myTestBean.getClass();
Field ageField = aClass.getDeclaredField("age");
ageField.setAccessible(true);//暴力反射。访问私有属性
int age = (int) ageField.get(myTestBean);
Log.d("AAA",age+"岁");
} catch (Exception e) {
e.printStackTrace();
}
热身准备完毕,下面我们开始正式动工。
涉及下面四个类
先定义两个注解
OnClick 点击的注解
/**
* view点击的注解Annotation
* 位置在方法上
* 运行时起作用
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
int [] value();
}
ViewId 查找id的注解
/**
* view的注解Annotation
* 位置在属性上
* 运行时
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewId {
int value();
}
public class ViewUtils {
public static void init(Activity activity){
init(new ViewFinder(activity),activity);
}
public static void init(View view){
init(new ViewFinder(view),view);
}
public static void init(View view,Object o){
init(new ViewFinder(view),o);
}
/**
* 为了兼容
* @param finder
* @param o
*/
private static void init(ViewFinder finder,Object o){
//事件监听
initEvent(finder,o);
//属性监听
initField(finder,o);
}
private static void initField(ViewFinder finder, Object o) {
Class<?> aClass = o.getClass();
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
//获取属性上面的注解
ViewId annotation = field.getAnnotation(ViewId.class);
if(annotation!=null){
int viewId = annotation.value();
//通过注解上的value值获取view
View view = finder.findViewById(viewId);
if(view!=null){
try {
field.set(o,view);//设置属性
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
private static void initEvent(ViewFinder finder, Object o) {
Class<?> aClass = o.getClass();
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
method.setAccessible(true);
OnClick annotation = method.getAnnotation(OnClick.class);
if(annotation!=null){
int[] values = annotation.value();
for (int value : values) {
View view = finder.findViewById(value);
if(view!=null){
//点击时间
view.setOnClickListener(new DeclaredClickListener(method,o));
}
}
}
}
}
public static class DeclaredClickListener implements View.OnClickListener{
private final Method method;
private final Object o;
public DeclaredClickListener(Method method, Object o) {
this.method = method;
this.o = o;
}
@Override
public void onClick(View v) {
try {
method.invoke(o,v);
} catch (Exception e) {
e.printStackTrace();
try {
method.invoke(o,new Object[]{});//不传值走这个
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
}
ViewFinder 辅助类
private Activity mActivity;
private View mView;
public ViewFinder(Activity activity) {
mActivity = activity;
}
public ViewFinder(View view) {
mView = view;
}
public View findViewById(int id){
return mActivity==null?mView.findViewById(id):mActivity.findViewById(id);
}
使用方法
//查找属性
@ViewId(R.id.left_iv)
private ImageView imageView;
//在Activity的onCreat方法中调用
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
ViewUtils.init(this);
}
//点击事件,第一种方式无参
@OnClick(R.id.left_iv)
public void test(){
}
//点击事件,第二种方式有参
@OnClick({R.id.left_iv,R.id.left_tv})
public void test(View v){
switch (v.getId()){
case R.id.left_iv:
break;
case R.id.left_tv:
break;
}
}