官网:https://developer.android.com/topic/libraries/data-binding/index.html
http://blog.csdn.net/qibin0506/article/details/47393725
记得在博客《高逼格UI-ASD(Android Support Design)》的开始曾经说过,Android最新推出了一个官方的数据绑定框架-Data Binding Library。现在github上也有很多三方的数据绑定框架,但是我们为什么要选择官方的呢?恩,答对了。就是因为是官方的,三方的东西说不定什么时候作者一步高兴就停止更新了,官方的就不一样了,我们可以看到它渐渐的稳定起来。好了废话不多说,从这篇博客开始,我们就来了解一下android最新给我们带来的数据绑定框架——Data Binding Library。数据绑定框架给我们带来了更大的方便性,以前我们可能需要在Activity
里写很多的findViewById
,烦人的代码也增加了我们代码的耦合性,现在我们马上就可以抛弃那么多的findViewById
。说到这里,有人可能会有个疑问:我使用一些注解框架也可以不用findViewById
啊,是的,但是注解注定要拖慢我们代码的速度,Data Binding则不会,官网文档说还会提高解析XML的速度,最主要的Data Binding并不是单单减少了我们的findViewById
,更多好处请往下看文章。
一、环境
在开始使用新东西之前,我们需要稍微的配置一下环境,这里要求你的Android Studio版本是1.3+,使用eclipse的同学暂时还没有办法使用该框架,请换用Android Studio。还有,在开始之前,请更新你的Support repository
到最新的版本。
万事俱备,那我们就开始搭配环境!
新建一个project
,在dependencies
中添加以下依赖
- 1
- 1
新建module
,并且在module
的build.gradle文件中添加
- 1
- 2
- 1
- 2
ok,到现在为止,我们的环境就准备完毕了,下面我们就开始Data Binding的学习啦。
二、Data Binding尝试
在代码开始,我们并不直接进入新东西的讲解,而且以一段代码展现Data Binding的魅力。
首先我们需要一个Java bean
,很简单,一个学生类。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
再来看看我们布局文件怎么写:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
可以看到我们的xml布局和以前还有有一定的差别的,但是差别也不是很大。
最后来看看Activity
怎么写。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Activity
的代码非常简单,就添加了两行代码,而且,值得注意的是:我们并没有findViewById
然后再去setText
。
这段小代码运行的结果大家可能已经猜到了,就是在界面上显示loader
和山东莱芜
两句话。
)
在看完小实例后,大家是不是感觉棒棒哒? 没有了之前的find控件,没有了setText,Activity
代码更加简洁明了!
下面开始,我们进入Data Binding的学习!
三、 初始Data Binding
上面的代码算是带领我们进入了Data Binding的世界,那我们先从布局文件开始入手Data Binding吧。再来看看上面的布局文件。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
我们的根节点变成了layout
,在layout
的子节点中分成两部分,第一部分是data
节点,第二部分才是我们之前的根节点,在data
节点下我们又定义了一个variable
,
从名称上看,这应该是一个变量,变量的名称是stu
,类型是org.loader.androiddatabinding.Student
,这类似我们在Java文件中这么定义:
- 1
- 1
ok,这样很好理解了吧,不过这里要写Student
完整的包名,一个还好,如果这里我们需要多个Student
呢?要累死? NO,NO,NO,我们还可以向写java文件那样导入包。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
这样写,就类似于java的
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
既然变量我们定义好了,那该怎么使用呢?还是看上面的xml文件。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
恩,注意看两个TextView
的android:text
,它的值是一个以@
开始,以{}包裹的形式出现,而内容呢?是stu.name
。stu就是我们上面定义的variable
,
name还记得吗?是我们Student
类中的一个变量。其实这里就会去调用stu.getName()
方法。
好了,很快,我们就入门了Data Binding,下面让我们来多定义几个变量试试看。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
来看看定义的变量,多了好几个,有一个String
类型的变量我们并没有导包,这里说明一下,和在java里一样,java.lang
包里的类,我们是可以不用导包的,再往下,一个boolean
和int
类型的变量,都是java基本类型的,所以说嘛,在这里定义变量,你就想成是在java里定义就ok。
再来看看这几个TextView
,第二个,我们直接使用@{str}
来为android:text
设置成上面定义个str
的值,继续往下要注意了,我们使用了
- 1
- 1
来设置了一个int
类型的变量,大家都知道我们在给android:text
设置int
类型的值时一定要转化为String
类型,要不它就认为是资源文件了,这里我们还学到了一点,在xml中,我们不仅可以使用变量,而且还可以调用方法!
四、 变量定义的高级部分
在上面,我们学会了如何去在xml中定义变量,但是不知道你发现没?我们没有定义像List
、Map
等这样的集合变量。那到底能不能定义呢?答案肯定是可以的,而且定义的方式和我们上面的基本一致,区别就在于我们还需要为它定义key的变量,例如:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
这段代码比较长,但是我们仅关心那几个集合和数组,可以看到我们定义集合和定义普通变量一样,只不过这里我们还指定了一些的泛型,例如:ArrayList<String>
。
下面我们还为下面使用这些集合准备了几个key,也都是变量。
继续看看怎么使用,和我们在java中使用不同,这里都是以:集合变量名[key]的形式使用,如果你的key是一个字面字符串可以使用反引号,也可以使用转义后的双引号。恩,这里也没有什么可以说的了,大家多看几遍就掌握了,都是概念性的东西,记住就ok。
五、在java代码中使用
前面定义了这么多变量,但是我们还没有给他们赋值!在哪赋值呢?肯定是在java代码中使用了,大部分情况我们还是在Activity
中去使用它,以前我们都是在onCreate
方法中通过setContentView
去设置布局,但现在不一样了,现在我们是用过DataBindingUtil
类的一个静态方法setContentView
设置布局,同时该方法会返回一个对象,什么对象?这个对象有点特殊,它是一个自动生成的类的对象,看下面:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
看到ActivityMainBinding
了吗?就是它!那自动生成有什么规则了没?当然有了,记好了
将我们布局文件的首字母大写,并且去掉下划线,将下划线后面的字母大写,加上Binding组成。
看看上面的类,是不是符合这个规则。继续看看这个对象哪来的,是通过
- 1
- 1
返回的,DataBindingUtil.setContentView的两个参数分别是当前Activity
和布局文件。那接下来,就是我们关心的给变量赋值了。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
一连串的binding.setXXX,这个XXX是什么呢?就是我们在xml中定义的那些变量首字母大写了!也没好好说的吧,多看几遍。
六、 表达式
短暂的幸福时光,我们还是要告别java代码了,继续回到xml中,这一块,我们来学习一下表达式,什么?这玩意在xml中还支持表达式!
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
还记得上面我们定义了一个boolean的变量没有用到,这里我们就用到了,看好android:text
,这里是一个三元表达式,如果error是true,则text就是error,否则是ok。这里还支持null合并操作,什么是null合并,相信看一眼你就知道了
- 1
- 1
简单解释一下,如果str是null,text的值就是str本身,否则就是”not null”。
它还支持一下表达式:
- Mathematical + - / * %
- String concatenation +
- Logical && ||
- Binary & | ^
- Unary + - ! ~
- Shift >> >>> <<
- Comparison == > < >= <=
- instanceof
- Grouping ()
- Literals - character, String, numeric, null
- Cast
- Method calls
- Field access
- Array access []
- Ternary operator ?:
但是它不支持一下表达式:
- this
- super
- new
- Explicit generic invocation
七、 其他遗漏点
说到这里,xml中的事情基本算完了,但是还有几个小地方没有说,顺便说一下。
1. 设置别名
假如我们import了两个相同名称的类咋办?别怕,别名来拯救你!例如:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 自定义Binding名称
还记得系统为我们生成好的那个binding类名吗?如果只能使用那样的是不是有点太暴力了?好在google对我们还算友好了,允许我们自定义binding名称,定制名称也很简单,就是给data一个class字段就ok。
例如:
- 1
- 2
- 3
- 1
- 2
- 3
那么:DataBindingUtils.setContentView返回的binding类就是:你的应用包名.Custom
。
八、事件绑定
大家都知道,在xml中我们可以给button
设置一个onClick
来达到事件的绑定,现在DataBinding也提供了事件绑定,而且不仅仅是button
。
来看一下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
定义了一个EventHandlers
类型的handlers
变量,并在onClick的时候执行EventHandlers
的handleClick
方法。
继续看看EventHandlers是怎么写的。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
很简单,就是简单的Toast
了一下,这里要注意的是,handlerClick
方法需要一个View
的参数。
九、 数据对象
我们学会了通过binding为我们的变量设置数据,但是不知道你有没有发现一个问题,当我们数据改变的时候会怎样?数据是跟随着改变呢?还是原来的数据呢?这里告诉你答案:很不幸,显示的还是原来的数据?那有没有办法让数据源发生变化后显示的数据也随之发生变化?先来想想ListView
是怎么做的, ListView
的数据是通过Adapter
提供的,当数据发生改变时,我们通过notifyDatasetChanged
通过UI去改变数据,这里面的原理其实就是内容观察者,庆幸的是DataBinding也支持内容观察者,而且使用起来也相当方便!
BaseObservable
我们可以通过Observable的方式去通知UI数据已经改变了,当然了,官方为我们提供了更加简便的方式BaseObservable
,我们的实体类只需要继承该类,稍做几个操作,就能轻松实现数据变化的通知。如何使用呢? 首先我们的实体类要继承BaseObservale
类,第二步在Getter
上使用注解@Bindable
,第三步,在Setter
里调用方法notifyPropertyChanged
,第四步,完成。就是这么简单,下面我们来实际操作一下。
首先定义一个实体类,并继承BaseObservable
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
观察getName方法,我们使用了@Bindable
注解,观察setName,我们调用了notifyPropertyChanged
方法,这个方法还需要一个参数,这里参数类似于R.java
,保存了我们所有变量的引用地址,这里我们使用了name。
再来看看布局文件。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
不多说了,我们给TextView
设置了文本,还有点击事件。Activity,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
这段代码,首先显示的是loader,当我们点击TextView
时,界面换成qibin。
ObservableFields家族
上面使用BaseObservable
已经非常容易了,但是google工程师还不满足,继续给我们封装了一系列的ObservableFields
,这里有ObservableField
,ObservableBoolean
,ObservableByte
,ObservableChar
,ObservableShort
,ObservableInt
,ObservableLong
,ObservableFloat
,ObservableDouble
,ObservableParcelable
ObservableFields的使用方法就更加简单了,例如下面代码,
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
很简单,只有三个ObservableField变量,并且没有getter和setter,因为我们不需要getter和setter。
在xml中怎么使用呢?
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
也很简单,直接使用变量,那怎么赋值和取值呢?这些ObservableField都会有一对get
和set
方法,所以使用起来也很方便了:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
也不多说了。
Observable Collections
既然普通的变量我们有了ObservableFields的分装,那集合呢?当然也有啦,来看着两个:ObservableArrayMap
,ObservableArrayList
。使用和普通的Map、List基本相同,直接看代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
在布局中,使用方式和普通的集合一样,如果看不太懂,可以往上翻博客,看上面的集合是怎么使用的。
在来看java文件,怎么设置数据,
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
哦,太简单了,简直和List
、Map
使用方法一模一样!!!
好了,不多说了,大家也都看累了吧。 那就先说到这里,其他的下篇博客我们继续学习。
demo源码下载,戳这里