欢迎各位加入个人Android开发群[257053751]java
Android开发中,有一个让人又爱又恨的方法叫findViewById(int);我想若是你是一民Android开发者,必然知道这个方法,git
为何说findViewById(int);让人又爱又恨呢?想必你们也是颇有感触。
写一个布局,用Java代码写和用xml文件写,完成速度彻底是没法比拟的。xml布局太方便了。
一样的,想获取一个控件的对象,若是你是使用的xml布局文件写的布局,那么你必须调用findViewById()这个方法。
github
TextView t = (TextView) findViewById(R.id.x);
这是咱们最多见的 获取xml布局中一个textview对象的过程。
那么问题就来了,这特么奇葩的方法名也太长了吧!!!好吧,其实人家名字起的也没有错,要描述清楚这函数的含义,也必须这么多个字母。express
但是你丫的返回一个View让我用的时候还得强转,这也太麻烦了吧。我一行代码总共也就80列(Eclipse默认),缩进八格(方法写在类里面,语句写在方法里面),
就算像上面的例子textView对象只有一个字母,id也只有一个字母,这一个初始化也要占我54列了。要是变量名再长点,缩进层次再深点,这一个初始化就两行了。
一个界面至少也有四个控件吧,这么复杂的初始化,太坑爹了。
有问题总会有对应的解决办法,下面我就向你们介绍一下KJFrameForAndroid框架使用注解解决这种麻烦。框架
KJFrameForAndroid框架项目地址:https://github.com/kymjs/KJFrameForAndroid。函数
从jdk1.5开始,Java提供了注解的功能,容许开发者定义和使用本身的注解类型,该功能由一个定义注解类型的语法和描述一个注解声明的语法,读取注解的API,一个使用注解修饰的class文件和一个注解处理工具组成。
首先,你须要接受一个关键字@interface ,噢,它可不是接口定义关键字,更不是OC里面的@interface关键字,是Java中表示声明一个注解类的关键字。
使用@interface 表示咱们已经继承了java.lang.annotation.Annotation类,这是一个注解的基类接口,就好像Object类,如今你只须要知道它的存在就好了。
还有一条规定:在定义注解时,不能继承其余的注解或接口。
那么,这就是最简单的一个注解类
工具
public @interface MyAnnotation { }
然而一般在使用时咱们都会给这个注解类加上两个注解:布局
@Target(ElementType.FIELD)、@Retention(RetentionPolicy.RUNTIME)
ElementType、RetentionPolicy是两个枚举类,ElementType.FIELD表示咱们须要注解的是一个字段,如下是摘自JDK1.6文档中的介绍:this
如下为KJFrameForAndroid框架中绑定控件注解部分的定义与使用:spa
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface BindView { public int id(); public boolean click() default false; }
@BindView(id = R.id.x, click = true) private TextView t;
咱们能够看到,除了明显减小了代码量,还使得代码结构更加清晰。
其中,定义部分的id() 表示注解接受一个int类型的数据做为id所对应的值(就如使用中的id = R.id.xxx);
同理,定义部分的click表示接受一个Boolean类型的数据做为click对应的值,还能够设置一个默认值使用default修饰;
咱们已经知道了注解怎么定义和使用,接下来就应该知道怎么处理了。
上面已经说了,bindview注解能够接受一个int类型的值和一个Boolean类型的值,那么这两个值接受了之后如何获取呢?
其实获取的方式很简单就是经过一个BindView类型的对象,调用这个对象来自声明中定义的两个方法——>id()或click()方法。
如今就有一个问题了,注解类型是不能直接new对象的,那么这个BindView对象从哪里来呢?
这时就须要用到Java的反射机制。咱们知道,每个继承自Object类的类都会继承一个getClass()方法,下面看一下这个方法的原型:
/** * Returns the unique instance of {@link Class} that represents this * object's class. Note that {@code getClass()} is a special case in that it * actually returns {@code Class<? extends Foo>} where {@code Foo} is the * erasure of the type of the expression {@code getClass()} was called upon. * <p> * As an example, the following code actually compiles, although one might * think it shouldn't: * <p> * <pre>{@code * List<Integer> l = new ArrayList<Integer>(); * Class<? extends List> c = l.getClass();}</pre> * * @return this object's {@code Class} instance. */ public final native Class<?> getClass();
是一个native方法,根据注释咱们知道,这个方法返回的是该类的Class对象,同时也是该类的二进制对象。
Class中有一个方法叫getDeclaredFields(),是用来返回这个类的所有字段,返回类型是Field[]
经过Field对象的getAnnotation(Class<?>)方法,咱们能够获取到任何一个Class的对象,经过getAnnotation(Class<?>),咱们就能够获取到BindView的对象了。
例如:
Field[] fields = currentClass.getClass().getDeclaredFields(); for(int i = 0; i < fields.length; i++){ BindView bindView = field.getAnnotation(BindView.class); int viewId = bindView.id(); //这是咱们传的id boolean clickLis = bindView.click(); //这是咱们传的click }
至此,咱们已经了解了注解,而且知道怎么使用,怎么处理注解了,如今只剩下最后一个问题:在项目中使用。
很简单,传一个Activity对象,调用findViewById()不就好了。
因而,咱们能够这样
activity.findViewById( bindView.id() );
最后在咱们的Activity中调用这个函数就OK了。
如下是Android应用框架KJFrameForAndroid中使用注解绑定控件的核心代码:
/** * @param currentClass * 当前类,通常为Activity或Fragment * @param sourceView * 待绑定控件的直接或间接父控件 */ public static void initBindView(Object currentClass, View sourceView) { // 经过反射获取到所有属性,反射的字段多是一个类(静态)字段或实例字段 Field[] fields = currentClass.getClass().getDeclaredFields(); if (fields != null && fields.length > 0) { for (Field field : fields) { // 返回BindView类型的注解内容 BindView bindView = field.getAnnotation(BindView.class); if (bindView != null) { int viewId = bindView.id(); boolean clickLis = bindView.click(); try { field.setAccessible(true); if (clickLis) { sourceView.findViewById(viewId).setOnClickListener( (OnClickListener) currentClass); } // 将currentClass的field赋值为sourceView.findViewById(viewId) field.set(currentClass, sourceView.findViewById(viewId)); } catch (Exception e) { e.printStackTrace(); } } } } }
其实安卓中的注解式绑定控件(也是所谓的IOC控制反转在安卓中的一种应用)其实本质的使用就是Java基础中反射的使用。值得一提的是,反射执行的效率是很低的
若是不是必要,应当尽可能减小反射的使用,由于它会大大拖累你应用的执行效率。顺带一提:我一直很排斥注解,由于类反射的效率过低了。如今有不少安卓应用开发框架,好比KJFrameForAndroid, xUtils, afinal, thinkAndroid,这些框架都是使用反射来起到注解绑定控件。更有的框架甚至是一切东西都使用注解去完成,我只能说注解便捷,但请慎用。