您的位置:首页 > 移动开发 > Android开发

Annotation自定义注解

2019-01-17 17:41 393 查看

前言:

Sometimes there is no next time, no time-outs, no second chances; sometimes it's now or never.

对于注解来说,其实就是用来打标记用的,在别的地方可以根据特殊的标记得到标记的东西(属性、类、方法等),对于这大家应该非常熟悉了,很多第三方框架使用也巨多,比如Dagger、ButterKnife等等,这些第三方注解框架,省略了findviewById(),不禁要问,难道真的不需要这行findviewById()代码就能在xml中得到控件实例吗?答案很明确,肯定不是如此。本篇文章就会给出答案,主要介绍自定义注解相关的知识,模仿实现findviewById功能。

1、自定义注解回顾

在开始实现 findviewById 功能之前,还是复习一下怎么自定义注解,以及注解有啥作用吧。

有这样一种场景:

主要工作是,要写一些注解类来标识SxtStudent类,然后通过注解把类中的信息跟表对应起来。比如上面SxtStudent类中,id为int类型,对应SQL中int(10);name为String类型,对应SQL中的varchar(10).。方式就是通过注解对类中的信息进行标注,写一个中间程序获取这个类中的注解信息,把注解解析出来,然后恰好可以作为SQL语句中拼接。

首先定义JavaBean类:

public class YdlStudent {  
 private int id;
 private String studentName;
 private int age;

 public int getId() {
   return id;
 }

 public void setId(int id) {
   this.id = id;
 }

 public String getStudentName() {
   return studentName;
 }

 public void setStudentName(String studentName) {
   this.studentName = studentName;
 }

 public int getAge() {
   return age;
 }

 public void setAge(int age) {
   this.age = age;
 }
}

然后定义类与表对应转换的注解:

/**
* 针对表的注解
* 代表类与表之间对应转换
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
public @interface YdlTable {
 String value();
}

因为表只需要一个表名即可,所以注解中只需要一个信息,这里使用String value();标识。

这个时候就可以对类进行注解了,这里对类的注解可以通过反射技术获取注解信息,然后作为创建的表名称。

然后还可以对属性添加注解,定义与属性关联的注解类:

/**
* 针对属性的注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
public @interface YdlFiled {
 String columName();//列名
 String type();//数据类型
 int length();//年龄
}

这里注解里面的内容是根据Student类和SQL来写的,SQL中的列名称定义为String columName();SQL中的数据类型为String type();SQL中的数据类型占的长度定义为int length();

然后在YdlStudent类中对属性添加注解:

这里

@YdlFiled(columName = "id",type = "int",length = 10)

private int id;

理解为:YdlStudent中属性id对应表中的字段id这一列,sql中的类型为int,int长度为10。

@YdlFiled(columName = "sname",type = "vachar",length = 10)

private String studentName;

理解为:YdlStudent中属性studentName对应表中的字段sname这一列,sql中的类型为vachar,vachar长度为10。

这样标识以后,就可以写一个解析程序来解析YdlStudent类,获取注解相关信息来创建表了:

public class Main {
 public static void main(String[] args) {
   Class clazz = YdlStudent.class;
   Annotation[] annotations = clazz.getAnnotations();
   for (Annotation annotation : annotations) {
     // 获取所有的【类】注解
     System.out.println(annotatio
5ac
n);
   }

   // 或得类的指定注解
   YdlTable table = (YdlTable) clazz.getAnnotation(YdlTable.class);
   // table.value()直接获取注解内容名称---@YdlTable(value = "tb_student")//可以通过反射读取这里,然后创建对应的表
   System.out.println(table.value());

   try {
     // 根据名称获取属性对象
     Field field = clazz.getDeclaredField("studentName");
     // 获取该属性上面的注解
     YdlFiled ydlFiled = field.getAnnotation(YdlFiled.class);
     // 获取注解属性的内容。
     System.out.println(ydlFiled.columName() + "---" + ydlFiled.type()
         + "---" + ydlFiled.length());

     // 同理可以获取 YdlStudent 类中 id、age所对应的注解信息

   } catch (Exception e) {
     e.printStackTrace();
   }

   // 根据上方获取到的表名、属性(字段)信息等拼接Sql语句。
 }
}

这里分段来讲:

   Class clazz = YdlStudent.class;
   Annotation[] annotations = clazz.getAnnotations();
   for (Annotation annotation : annotations) {
     // 获取所有的【类】注解
     System.out.println(annotation);
   }

这里表示通过反射获取所有的类注解,因类注解只有一个:

所以打印结果为:@itydl03.YdlTable(value=tb_student)

   // 或得类的指定注解
   YdlTable table = (YdlTable) clazz.getAnnotation(YdlTable.class);
   // table.value()直接获取注解内容名称---@YdlTable(value = "tb_student")//可以通过反射读取这里,然后创建对应的表
   System.out.println(table.value());

表示通过反射获取类的指定注解,clazz.getAnnotation(YdlTable.class);表示指定获取YdlTable类的注解。然后table.value()直接获取注解内容名称---@YdlTable(value = "tb_student")。获取的内容就是tb_student,打印结果:

tb_student

try {
     // 根据名称获取属性对象
     Field field = clazz.getDeclaredField("studentName");
     // 获取该属性上面的注解
     YdlFiled ydlFiled = field.getAnnotation(YdlFiled.class);
     // 获取注解属性的内容。
     System.out.println(ydlFiled.columName() + "---" + ydlFiled.type()
         + "---" + ydlFiled.length());

     // 同理可以获取 YdlStudent 类中 id、age所对应的注解信息

   } catch (Exception e) {
     e.printStackTrace();
   }

clazz.getDeclaredField("studentName");表示根据名称获取属性对象,返回值得到一个Field field 表示属性 。 在YdlStudent类中  private String studentName; 属性对应的注解为

@YdlFiled(columName = "sname",type = "vachar",length = 10)

field.getAnnotation(YdlFiled.class);就表示获上边属性YdlFiled注解类中的@YdlFiled(columName = "sname",type = "vachar",length = 10)的所有注解信息;返回值为YdlFiled类型。他可以理解为columName = "sname",type = "vachar",length = 10的注解信息的javaBean。最后打印:

sname---vachar---10

最后再把所有log给出:


2、仿造findviewById()

有了上边的基础回顾,下面就不写那么细致了。

首先定义一个注解类,ViewById:

@Target(value={ElementType.FIELD})//FIELD表示的是成员变量级别可以使用该注解
@Retention(RetentionPolicy.RUNTIME)//RUNTIME级别可以被反射读取注解,都是运行时
public @interface ViewById {
   int value();
}

然后在MainActivity中使用该注解:

public class MainActivity extends AppCompatActivity {

   @ViewById(R.id.tv)
   private TextView mTextView;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       mTextView
16c8
.setText("自定义注解");
   }
}

看着好像挺熟悉的,跟使用第三方框架差不多,但是运行程序肯定报错。这是因为MainActivity类跟自定义注解压根就没有什么连接关系。那么接下来就建立连接关系。而这个中介就是反射。

自定义ViewUtils类:

public class ViewUtils {
   public static void inject(Activity activity) {
       // 1.获取所有的属性
       Field[] fields = activity.getClass().getDeclaredFields();
       // 2.过滤关于 ViewById 属性
       for (Field field : fields) {
           //获取注解封装---ViewById类型的注解。相当于对ViewById所标识属性的封装类
           ViewById viewById =  field.getAnnotation(ViewById.class);
           if(viewById != null){
               // 3.findViewById
               View view = activity.findViewById(viewById.value());
               field.setAccessible(true);
               try {
                   // 4.反射注入
                   // activity 属性所在类,view 代表的是属性的值
                   field.set(activity,view);
               } catch (IllegalAccessException e) {
                   e.printStackTrace();
               }
           }
       }
   }
}

代码注释非常详细了,主要功能就是反射解析注解,然后在这里进行了findviewById操作获取View,最后再注入到属性对象里面field.set(activity,view);

此时需要在MainActivity中加入如下代码:

ViewUtils.inject(this);

表示注入当前的MainActivity,只有注入了才能使用自定义的注解“框架”。

为了更直观,把MainActivity中代码稍微修改如下:

public class MainActivity extends AppCompatActivity {

   //注解属性,解析类就是解析这里带注解的属性
   @ViewById(R.id.tv)
   private TextView mTextView;

   @ViewById(R.id.tv2)
   private TextView mTextView2;

   //非注解属性
   private int age;
   private String name;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       ViewUtils.inject(this);

       mTextView.setText("自定义注解");

       mTextView2.setText("我是SuperMan");
   }
}

其中private int age;和private String name;两个属性虽然在注解解析工具类会获取到,但是我们已经对其做了过滤处理。此时运行程序:

到此为止,注解相关的知识就讲解完了。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android 自定义 注解