Android: 使用注解findViewById

今天模拟一个使用注解来实现findViewById的小demo, ps: 其实只能算是笔记

注解Annotation

* Java中提供,java代码中用来描述,关联任意信息和数据的一种方法(或规则)
* 分类
    * 按平台
        * JDK自带
            * @Override,表示覆写了父类方法
            * @Deprecation,表示过时了
            * @suppressWarning,忽视警告,suppress,镇压
        * 第三方平台
            * Android,@Nullable,可以为空的...
        * 自定义
    * 按时机
        * RetentionPolicy.SOURCE,只在源代码显示,变异的时候丢弃
        * RetentionPolicy.CLASS,编译时会记录到字节码对象中,执行的时候丢弃
        * RetentionPolicy.RUNTIME,运行的时候存在,可以通过反射获取
* 注解的属性的数据类型
    * 八大基本数据类型,byte,short,char,int,long,float,double,boolean
    * 合法类型,String,Class,Annotation,Enumeration
* 注解可以使用的范围
    * 接口,类:ElementType.TYPE
    * 方法:ElemengType.METHOD
    * 字段:ElementType.FIELD
    * 局部变量:ElementType.LOCAL_VARIABLE
    * 方法声明的参数:ElementType.PARAMETER
    * 构造方法:ElementType.CONSTRUCTOR
    * 包声明:ElementType.PACKAGE

思路

* 在Activity中声明View的成员变量
* 给View类型的成员变量添加注解
* 在onCreate方法的setContentView后运行反射解析注解的方法;
    * 确保在使用控件前必须获取到对象
* 在inject方法中反射获取到acticity的对象,通过注解的值给activity的字段赋值

代码

* 定义一个注解类型:
    package com.kas.utils;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    @Target({ElementType.FIELD}) //表示使用于字段
    @Retention(RetentionPolicy.RUNTIME)//表示是运行时注解,
    public @interface ViewId {
        int value();//表示值是int类型
    }
* Activity中添加注解
    public class MainActivity extends Activity {
        @ViewId(R.id.bt)
        private Button button;
        @ViewId(R.id.tv)
        private TextView tView;
        private int num;

        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            InjectUtils.inject(this); //--->必须在setContentView后调用这个方法

            button.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this, "按钮被点击了", 0).show();
                    tView.setText("按钮被点击了哦");
                }
            });
        }
    }
* InjectUtils.inject(Activity activity)
    public class InjectUtils {
        public static void inject(Activity activity){//定义为静态方法,可以直接类名.调用
            Class clazz = activity.getClass();  //先获取activity的字节码对象
            Class mViewClass = View.class;      //获取View的字节码对象,用来判断我们获取的成员变量是不是View对象
            Class type = null;                  //定义一个Class类型的type引用,用来表示获取到的字段的类型
            Field[] fields = clazz.getDeclaredFields(); //获取activity对象中的所有字段的引用
            for (Field field : fields) {                //遍历数组
                /**
                 * 1.设置字段为可以访问
                 * 如果不设置,在获取私有字段的时候,就会抛出异常
                 * java.lang.IllegalAccessException: access to field not allowed
                 */
                field.setAccessible(true);          //设置字段为可以访问(ps,如果不设置,而该字段对象是private,就会导致抛异常)
                //2.获取字段的类型         
                type = field.getType();             //获取字段的字节码对象
                /**
                 * 3.判断该字段是否为View的子类
                 * 返回值为true,表示mViewClass与type表示的类和接口相同,或者mViewClass是type的父类
                 */
                if (!mViewClass.isAssignableFrom(type)) {   //判断该字段的类型是不是View或是View的子类
                    System.out.println(field + "不是View的子类");
                    continue;
                }
                /**
                 * 判断该字段是否有注解,flag为true表示添加了注解,false表示没有添加注解
                 * present,提出,介绍,呈现
                 */
                boolean flag = field.isAnnotationPresent(ViewId.class);//判断字段是否有ViewId的注解
                if (!flag) {
                    continue;
                }
                /**
                 * 获取注解对象,如果注解对象为null,则结束本次循环
                 */
                ViewId viewId = field.getAnnotation(ViewId.class);  //获取field对应的注解对象
                if (viewId == null) {
                    continue;
                }
                /**
                 * 获取注解表示的id
                 */
                int valueId = viewId.value();       //获取注解对象的值
                try {
                    View view = activity.findViewById(valueId); //拿到值后,调用activity中的findViewById,获取View对象

                    //如果findViewById查找的结果是null,说明id不存在或用户恶意攻击,直接甩出一个异常
                    if (view == null) {
                        throw new IllegalStateException("there is a null result when findViewById for " + field);
                    }
                    field.set(activity, /*type.cast(view)*/view);//设置字段field的值, type.cast(view),表示将view强转为type类型
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

小结

* Annotation
    * 定义注解:
        public @interface ViewId{
            int value();
            String desc();
        }
    * 给注解声明值
        - 注解有多个属性的时候
        @ViewId(desc = "button",value = R.id.bt)
        private Button button;
        //值随便填
    * 反射获取值
        boolean flag = field.isAnnotationPresent(ViewId.class);
        ViewId viewId = field.getAnnotation(ViewId.class);
        int valueId = viewId.value(); //方法与定义注解的时候相互对应
        String desc = viewId.desc()
* Field,
    * boolean getAnnotationParent(Class annotation)
        * 判断this字段是否包含了annotation注解
    * Annotation getAnnotationParent(Class annotation)
        * 获取制定的annotation注解对象
        * int annotation.value();//获取注解对象对应的值
    * Class<?> getType()
        * 获取this字段的字节码对象
    * void setAccessible(boolean acc)
        * 设置为可以访问的,如果获取到的是私有的成员变量,如果不提前设置就操作的话,就会抛出异常 IllegalAccessException: access to field not allowed
        * 访问出错,不允许访问
* Class
    * boolean isAssignableFrom(Class other)
        * 判断this是不是other类型或是other的子类
        * assignable,可分配的
    * T cast(Object obj),
        * 将obj对象强转为T,(this表示的就是T类型的一个对象)        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值