Android: 使用注解findViewById

来源:互联网 发布:转行做程序员 编辑:程序博客网 时间:2024/04/29 04:56

今天模拟一个使用注解来实现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