Android注解式绑定控件,没你想象的那么难

Android开发中,有一个让人又爱又恨的方法叫findViewById(int);我想如果你是一民Android开发者,必然知道这个方法。
那么为什么让人又爱又恨呢?想必大家也是很有感触。
写一个布局,用Java代码写和用xml文件写,完成速度完全是无法比拟的。xml布局太方便了。
同样的,想获取一个控件的对象,如果你是使用的xml布局文件写的布局,那么你必须调用findViewById()这个方法。

TextView t = (TextView) findViewById(R.id.x);

这是我们最常见的 获取xml布局中一个textview对象的过程。
那么问题就来了,这特么奇葩的方法名也太长了吧!!!好吧,其实人家名字起的也没有错,要描述清楚这函数的含义,也必须这么多个字母。
可是你丫的返回一个View让我用的时候还得强转,这也太麻烦了吧。我一行代码总共也就100列(Eclipse默认),缩进八格(方法写在类里面,语句写在方法里面),
就算像上面的例子textView对象只有一个字母,id也只有一个字母,这一个初始化也要占我54列了。要是变量名再长点,缩进层次再深点,这一个初始化就两行了。
一个界面至少也有四个控件吧,这么复杂的初始化,太坑爹了。
有问题总会有对应的解决办法,下面我就向大家介绍一下使用注解解决这种麻烦。

了解注解:

从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文档中的介绍:

使用注解:

以下为KJFrameForAndroid框架中绑定控件注解部分的定义与使用:

@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
}

在Android项目中应用:

至此,我们已经了解了注解,并且知道怎么使用,怎么处理注解了,现在只剩下最后一个问题:在项目中使用。
很简单,传一个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,这些框架都是使用反射来起到注解绑定控件。
更有的框架甚至是一切东西都使用注解去完成,我只能说注解便捷,但请慎用。

时间: 2024-10-08 21:45:22

Android注解式绑定控件,没你想象的那么难的相关文章

Android注解式绑定控件BindView

转载请注明 出处:http://blog.csdn.net/u012975705/article/details/49637401 BindView.java package com.practice.noyet.coolweather.util; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;

ButterKnife注解式绑定控件

Butter Knife Android为控件设计的注解绑定库. github地址:https://github.com/JakeWharton/butterknife 添加依赖:(具体看github官网) dependencies { implementation 'com.jakewharton:butterknife:9.0.0-rc2' annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0-rc2' } 注意:·修

谷歌推出Android 响应式布局控件 FlexboxLayout -盒子模型

今天github 排行榜上突然出现了 谷歌最新推出的Android 最新控件FlexboxLayout . FlexboxLayout 究竟是什么东西呢? fexbox 也称为盒子模型,广泛用于前端开发,做过前端 web 的都知道Bootstrap 中有一套强大的 CSS 网格样式. Bootstrap 的出现 大大提高了前端开发的效率,并且引领了响应式布局.跨平台开发的潮流. FlexboxLayout  就是类似于 bootstrap 中见网格系统的 强大控件 先上几张谷歌示例程序的截图 F

Android IOC 之 注解绑定控件 以及事件 事例 代码简单注释讲解

想必开发过android的都使用过一些 框架,然而框架里都会使用一些,注解来帮助我们,节省大量的代码开发量.同时也使代码更加的简洁. 但是注解是通过java反射来实现的当然可能会牺牲掉一些性能. 以下是本人写的一个较为简单的绑定控件,并且触发其OnclickListem 事件. //直接先看下注解方法.比较简单,清晰 public class MainActivity extends FragmentActivity { <span style="white-space:pre"

java android布局里的控件值 反射绑定给实体类,实体类绑定给控件,表单提交绑定很有用

注意了:根据实际情况,添加实体里字段的类型,控件类型的判断才可使用.这里控件只有TextView EditText 实体类字段只有String int类型,带值的控件添加tag ,值和实体类的字段值一致 package ice.ui.service; import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import jav

Android常用酷炫控件(开源项目)github地址汇总

转载一个很牛逼的控件收集贴... 第一部分 个性化控件(View) 主要介绍那些不错个性化的 View,包括 ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.ProgressBar.TextView.ScrollView.TimeView.TipView.FlipView.ColorPickView.GraphView.UI Style 等等. 一.ListView android-pulltorefresh一个强大的拉动

Android UI-自定义日历控件

Android UI-自定义日历控件 本篇博客笔者给大家分享一个日历控件,这里有个需求:要求显示当前月的日期,左右可以切换月份来查看日期. 我们想一想会如何去实现这样的一个控件,有开源的,但可能不太满足我们的特定的需求,这里笔者自定义了一个,读者可以根据自己的需求来修改代码.下面来说一下实现的思路: 首先我们要显示当前月份,自然我们要计算出当前的日期,并且把每一天对应到具体的星期,我们会有以下效果: 我们先想一下这样的效果用什么控件可以实现?很自然可以想到用网格视图GridView,但这里笔者使

Android自定义控件之日历控件

Android自定义控件之日历控件 2015-10-23 Android开发中文站 三月份学习android,至今也有半年有余,中间也做过两个项目,但是依然感觉自己做的应用不是很有新意,比不上应用市场上那些应用如此绚丽.所以自己仍需继续努力.学习至今,仍感觉自定义控件是一块硬骨头,还没修炼到身后的内功,下面就切入正题,以一次项目的需求,来实现一个自定义的日历控件.效果图先来一发. 我们分析下效果图,然后确定我们的需求. (1).绘制星期的自定义View,用于标识日期的礼拜. (2).绘制日期的自

Android自定义控件——自定义组合控件

转载请注明出处http://blog.csdn.net/allen315410/article/details/39581055  前面几篇博文介绍了Android如何自定义控件,其实就是讲一下如何"从无到有"的自定义一个全新的控件,继承View或者继承ViewGroup,复写其相关方法,这种自定义控件的方式相对来说难度较大,而且并不是所有需要新控件的情况下,都要这样进行.有很多情况下,我们只要运用好Android给我提供好的控件,经过布局巧妙的结合在一起,就是一个新的控件,我称之为&