Android 注解的应用

 

        Java中的定义

         注解是JDK5 之后的新特性,是一种特殊的注释,它为我们在代码中添加信息提供了一种形式上的方法,使我们可以在稍后某个时候非常方便的使用这些数据。

       官方文档

        An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.

        注解是一种元数据, 可以添加到java代码中. 类、方法、变量、参数、包都可以被注解,注解对注解的代码没有直接影响

       Java中内置的注解

        JavaSE5内置了三种注解定义在java.lang包中:

@Override表示当前方法覆盖超类中的方法。如果你所写的方法和超类中的方法不同的话,编译器会报错。主要用于检查。
@Deprecated表明当前的元素已经不适用。当使用了注解为@Deprecated的元素时,编译器会报出警告。
@SuppressWarnings关闭不当的编译器警告。

        解析@Override,查看其源码示例:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}

       @Override源码上面有两个方法@Target和@Retention, @Target和@Retention的作用就是定义注解的作用范围和使用范围,这样的关键字称为元注解。

        元注解

         元注解有四个方法:@Retention@Target, @Inherited, @Documented

@Retention属性描述
SOURCE在源码中使用,注解将被编译器丢弃,只存在源码中,其功能是与编译器交互,用于代码检测,如@Override,@SuppressWarings,许多框架如Dragger就是使用这个级别的注解,这个级别的框架额外效率损耗发生在编译时。
CLASS

源码和字节码中使用,一般在字节码中使用,注解存在源码与字节码文件中,主要用于编译时生成而外的文件,如XML,Java文件等,这个级别需要添加JVM加载时候的代理(javaagent),使用代理来动态修改字节码文件(由于Android虚拟机并不支持所以本专题不会再做介绍,在Android中可以使用aspectJ来实现类似这个级别的功能)。

RUNTIME源码,字节码,运行均可使用,注解存在源码,字节码与Java虚拟机中,主要用于运行时反射获取相关信息,许多框架如OrmLite就是使用这个级别的注解,这个级别的框架额外的效率损耗发生在程序运行时。
@TargetElementType表明当前注解可以使用在哪种元素上,属性的值如 TYPEMETHODCONSTRUCTORFIELDPARAMETER
@Inherited 是否可以被继承,默认为false
@Documented 被修饰的注解会保存到javadoc里面

其中, @Retention是定义保留策略, 直接决定了我们用何种方式解析. SOUCE级别的注解是用来标记的, 比如Override, SuppressWarnings. 我们真正使用的类型是CLASS(编译时)和RUNTIME(运行时)

         自定义注解     

          代码示例 :

类型 参数名() default 默认值,其中默认值是可选的, 可以定义, 也可以不定义.
@interface是定义注解用的关键字

 /*
  *  绑定控件ID
  */

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
    int value();
    int ViewId() default 0;   //在开发过程中如果不需要需要赋值可以省略    
}

/*
 *  绑定布局
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContentView {
    int value();
}

/*
 *  绑定事件
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnClick {
    int[] value();
    int[] parentId() default 0;
}
  • 参数的类型只能是public或者不写两种访问修饰符
  • 注解参数必须有确切的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或0作为默认值是一种常用的做法

定义好了注解,如果我们直接来使用,是没有任何效果的,因为注解只是一段代码,并没有关联上我们的控件,所以我们要编写一个工具类来做关联

public class AnnotateUtils {

    public static void inject(Activity activity) {
        if (activity.getClass().isAnnotationPresent(ContentView.class)) {
            ContentView contentView = activity.getClass().getAnnotation(
                    ContentView.class);
            int layoutResID = contentView.value();// 获取布局的资源id
            activity.setContentView(layoutResID); // 设置布局内容
            injectViews(activity);                // 绑定view
        }
    }



    public static void injectViews(Activity activity) {
        try {
            Class<?> clazz = activity.getClass();
            Field[] fields = clazz.getDeclaredFields();           //获得Activity中声明的字段
            for (Field field : fields) {
                if (field.isAnnotationPresent(ViewInject.class)) {// 查看这个字段是否有我们自定义的注解类标志的
                    ViewInject inject = field.getAnnotation(ViewInject.class);
                    int id = inject.value();
                    if (id > 0) {
                        field.setAccessible(true);
                        field.set(activity, activity.findViewById(id));//给我们要找的字段设置值
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }
}

在工具类中,我们获取到了activity的Class,接着获得类中的所有字段,遍历字段,如果有ViewInject注解,则获取注解的中的值,通过获取并执行类中的方法(findViewById)来获得对应View的实例,最后把实例赋值给当前的字段,整个过程就完成了。

当我们需要使用注解的时候,我们只需要在定义View的时候,在View的字段上添加对应的注解,把Id传入,然后在onCreate方法中调用上面这个工具类的方法即可。


@ViewInject(R.id.bt)    //使用注解的时候,句末不能加“;”
private Button bt;
@ViewInject(R.id.tv)
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    AnnotateUtils.injectViews(this);
}
  • 如果注解中的值不是value,那么在进行注解是时候,需要给出对应的值的名字,假如我们在注解中做了如下定义:

    在使用的时候需要:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject{
    int id();
}

在使用时需要:


@ViewInject(id = R.id.buy) //使用注解的时候,句末不能加“;”
private Button bt;
@ContentView(value = R.layout.main)
public class MainActivity extends BaseActivity{

    @ViewInject(R.id.tv)
    private TextView mTv;

    protected void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState)
            AnnotateUtils.inject(this);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值