在安卓巴士上看到了这篇文章,感觉自己有点 落后了,赶紧学习了一下。
转载地址:http://www.apkbus.com/forum.php?mod=viewthread&tid=245694&extra=&_dsign=29ec4049
2015年的Google I/O 大会上发布了三个重要的支持库:
1
、
Material design
支持库:
Android Support Design
2
、百分比布局支持库:
Percent support lib
3
、数据绑定支持库:Data Binding Library
官方文档地址:
Data Binding Library有什么用?
获取Data Binding Library
需求与支持
使用Data Binding Library
基本使用流程
解决问题
一、数据刷新自动同步如何完成呢?
https://developer.android.com/intl/zh-cn/tools/data-binding/guide.html
每当有新东西出现我们都喜欢问有什么用?这可不是然并卵的东西,我们来了解下。
1
、是官方支持的
MVVM
模式框架
2
、可以直接在布局
xml
文件中绑定数据,无需再
findViewById
然后手工设置数据
3
、可以提高解析
XML
的速度
4
、
UI
与功能的解耦合
获取Data Binding Library
我们在项目的
build.gradle构建文件(不是
module
的
build.gradle
)里添加如下信息即可获取,当然没有下载的话会提示你下载:
[AppleScript]
纯文本查看
复制代码
1
2
3
4
5
6
7
|
dependencies
{
classpath
"com.android.tools.build:gradle:1.3.0"
classpath
"com.android.databinding:dataBinder:1.0-rc1"
}
|
由于依赖的项目在
jcenter
服务器中,所以在
repositories
中需要添加
jcenter
如下:
[AppleScript]
纯文本查看
复制代码
1
2
3
4
5
6
7
8
9
|
allprojects
{
repositories
{
jcenter
(
)
}
}
|
在需要使用支持库的
module
的
build.gradle
文件中添加插件申请:
[AppleScript]
纯文本查看
复制代码
1
2
3
|
apply plugin
:
'com.android.
application
'
apply plugin
:
'com.android.databinding'
|
需求与支持
需要:
Android Studio 1.3.0-beta1
或更高版本。
Gradle
插件版本为
1.3.0
以上
支持:
Android 2.1
(
API level 7+
)以上
版本:
官方文档上版本已经到了
1.0-rc1
,但是奇怪的是无法在
studio
上获取到。这个问题以后再研究,如果你们也遇到这个问题可以把版本换成
1.0-rc0
。
目前处于测试版,所以
API
还不稳定,随时可能会修改
API
接口,同时还有很多
BUG
。所以使用需要慎重之啊,不过既然是官方的必然会越来越好咯。如果你遇到了
BUG
可以到以下地址反馈
https://code.google.com/p/android-developer-preview/
使用Data Binding Library
这个支持库的使用属于比较复杂的,下面总结了一个基本的流程,详细的高级的用法后面再一一介绍。
基本使用流程
第一步:创建XML布局
创建一个布局
xml
文件,就像以前一样。然后这里我们需要做一些少少的修改,在这个框架下我们的思维要稍稍改变一下了,以前的布局
XML
只描述了布局,他是相对固定的东西,在Data Binding Library下我们的布局
XML
就像是一个类,他可以有变量也能进行一定的运算。其实Data Binding Library还真的给你生成了一个类似这样的类。
[XML]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
data
>
<
variable
name
=
"user"
type
=
"com.example.User"
/>
</
data
>
<
LinearLayout
android:orientation
=
"vertical"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
>
<
TextView
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:text
=
"@{user.firstName}"
/>
<
TextView
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:text
=
"@{user.lastName}"
/>
</
LinearLayout
>
</
layout
>
|
上面的是官方的示例布局代码
其中我们布局文件的根节点变成了
layout
,然后定义了变量
[XML]
纯文本查看
复制代码
1
2
3
4
5
|
<
data
>
<
variable
name
=
"user"
type
=
"com.example.User"
/>
</
data
>
|
在
data
元素中使用
variable
来声明在布局文件中使用的变量。
[XML]
纯文本查看
复制代码
1
|
<
variable
name=”user” type=”com.example.User”/>
|
Name
声明了变量的名称,
type
声明了变量的类型,变量可以为基本类型如
int
,也可以为集合或者对象。
View
中使用变量用
@{}
格式来调用,下面
Textview
使用了我们对象
user
的变量
firstName
。
[XML]
纯文本查看
复制代码
1
2
3
4
5
6
7
8
9
|
<
TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@{user.firstName}”/>
|
第二步:定义数据对象
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public
class
User {
public
final
String firstName;
public
final
String lastName;
public
User(String firstName, String lastName) {
this
.firstName = firstName;
this
.lastName = lastName;
}
}
|
就像我们平时所定义的实体类一样定义就可以了,也可以是带
getXxx(),setXxx()
方法的
JavaBeans
。
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public
class
User {
private
final
String firstName;
private
final
String lastName;
public
User(String firstName, String lastName) {
this
.firstName = firstName;
this
.lastName = lastName;
}
public
String getFirstName() {
return
this
.firstName;
}
public
String getLastName() {
return
this
.lastName;
}
}
|
在
XML
布局中
Textview
所设置的
android:text=
”
@{user.firstName}
”正是对应实体类中的变量
firstName
。框架会自动找到变量或者找到
getXxx
方法来获取。
[Java]
纯文本查看
复制代码
1
2
3
4
5
6
7
8
9
|
<TextView android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@{user.firstName}”/>
|
第三步:绑定数据
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
MainActivityBinding binding = DataBindingUtil.setContentView(
this
, R.layout.main_activity);
User user =
new
User(
"Test"
,
"User"
);
binding.setUser(user);
}
|
好啦,又是官方示例代码。在
activity
的
onCreate
方法中进行了基本的数据绑定。一句一句代码给你们解释。
[size=10.5000pt]
[size=10.5000pt]1、原来设置布局的
setContentView
方法我们现在不用了,改成了
DataBindingUtil.setContentView
[size=10.5000pt]
[size=10.5000pt]2、对于第一点的返回值是框架给我们生成的类,该类按照单词首字母大写的规则,布局的名字加上
Binding
组成。比如我们的布局是
main_activity.xml
,所以按照这个规则来转换的话就变成了
MainActivityBinding
。
[size=10.5000pt]
[size=10.5000pt]3、自动生成的类
ActivityMainBinding
其实就是代表了那个布局,里面包括了布局的
View
,我们声明的变量。我们可以通过这个对象获取到布局的元素。
[size=10.5000pt]
[size=10.5000pt]4、binding.setUser(user);
没错,这里的
setUser
方法是框架给我们自动生成的,每一个布局中声明的变量都会自动生成对应的
get,set
方法。
看到这里基本的使用教程就完毕了。下面通过实践来使用这个框架吧。
好的,刚刚学了基础不能做太复杂的,就做一个点击一下按钮
APKBUS
会长大一岁的小功能,这里也模拟了我们是数据更新。
首先我们来做一个自己的布局吧。
[XML]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
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
71
72
73
74
75
|
>
<
data
>
<
variable
name
=
"user"
type
=
"com.apkbus.mydatabinding.beans.User"
/>
<
variable
name
=
"buttonname"
type
=
"String"
/>
</
data
>
<
RelativeLayout
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
>
<
TextView
android:text
=
"@{user.name}"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:id
=
"@+id/textView"
/>
<
TextView
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:text
=
"@{String.valueOf(user.age)}"
android:id
=
"@+id/age_textView"
android:layout_alignParentTop
=
"true"
android:layout_centerHorizontal
=
"true"
/>
<
Button
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:text
=
"@{buttonname}"
android:id
=
"@+id/age_button"
android:layout_alignParentBottom
=
"true"
android:layout_alignParentLeft
=
"true"
android:layout_alignParentStart
=
"true"
/>
</
RelativeLayout
>
</
layout
>
|
在此布局定义了:
[size=10.5000pt]1、两个变量,一个是对象
User
,一个是
String
类型的
buttonname
。
[size=10.5000pt]2、两个
TextView
一个
Button
下面是我们的实体类
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public
class
User {
public
String name;
public
int
age;
public
User(String name,
int
age){
this
.name = name;
this
.age = age;
}
}
|
很简单就两个成员变量
然后是
Java
代码调用。
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
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
|
public
class
MainActivity
extends
AppCompatActivity {
private
User myUser;
private
ActivityMainBinding myBinding;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
myBinding = DataBindingUtil.setContentView(
this
, R.layout.activity_main);
myUser =
new
User(
"apkbus"
,
4
);
myBinding.setUser(myUser);
myBinding.setButtonname(
"APKBUS快快长大"
);
myBinding.ageButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
myUser.age++;
myBinding.setUser(myUser);
}
});
}
}
|
好的,现在我们跑起来看看。
![](http://www.apkbus.com/data/attachment/forum/201508/12/175049p78bbfowebw5o7to.png)
现在已经实现我们的想法了,但是看到这里的代码以及我们实现的过程遇到了几个问题。
1、数据改变不能刷新问题
当我尝试单方面的进行
myUser
对象数据更改的时候界面上的数据并不能自动刷新,然后我通过
setUser
再次设置就可以刷新了,这简直是烦恼啊!
2、数据类型问题
大家都看到了这里设置了我们
安卓巴士的年龄,是一个
int
类型的数据。
android:text="@{user.age}"
如果我们在布局中这样写是不会报错的(其实我感觉检查的功能还没做吧),然后跑起来就会报错啦,这是一个小疏忽平时
Java
中提示一下就不会发生了,但是这里不会提示,然后报错也是比较奇怪的。提示找不到
String
资源(这个奇怪现象后面再解释)。这里大家需要注意数据类型问题。
3、@{String.valueOf(user.age)}这样的表达式支持那些呢?
带着这些问题我们来进一步的学习
Data Binding Library
。
解决问题
一、数据刷新自动同步如何完成呢?
解决数据同步其实谷歌已经想到了,在这里提供了两个解决办法。
1、让实体类继承BaseObservable类
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
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
|
public
class
User
extends
BaseObservable{
public
String name;
public
int
age;
public
User(String name,
int
age){
this
.name = name;
this
.age = age;
}
@Bindable
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
notifyPropertyChanged(BR.user);
}
@Bindable
public
int
getAge() {
return
age;
}
public
void
setAge(
int
age) {
this
.age = age;
notifyPropertyChanged(BR.user);
}
}
|
说明:
1
、首先继承
BaseObservable
类
2
、给成员变量的
get
方法添加
@Bindable
注解
3
、在成员变量的
set
方法的最后调用方法
notifyPropertyChanged
,其中里面的参数是在
BR
类中记录的变量,那些变量就是我们在
XML
中声明的那些啦,类似于安卓资源管理的
R.java
文件,不过这里管理的是布局
XML
的变量,是自动生成的。
在这里我又有问题要问了,之前我的实体类这么简约,现在变这么长,感觉不爽!
那么有没有简约版的呢,因为我很懒不想弄这么多代码?
2、使用Observable数据类型
这是一个简约的方法,谷歌为我们提供了基于常用数据类型的
Observable
类型,就是我们熟悉的数据类型前面加上
Observable
组成。
例如:
Int
类型对应
ObservableInt
Boolean
类型对应
ObservableBoolean
其他基本类型类似以上写法。
而
String
类型对应的是
ObservableField<String>
,
ObservableField
是一个泛型
![](http://www.apkbus.com/data/attachment/forum/201508/12/175049fhc44t57044zifft.png)
我们可以看看他的源码
然后再看看ObservableInt
的源码
![](http://www.apkbus.com/data/attachment/forum/201508/12/175049wodaqalttqmmaqkm.png)
其他类型的源码基本类似。这样看来其实ObservableField
除了可以存放对象还可以代替其他所有基本类型来使用。
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
public
class
User {
public
ObservableField<String> name =
new
ObservableField<>();
public
ObservableInt age =
new
ObservableInt();
public
User(String name,
int
age){
this
.name.set(name);
this
.age.set(age);
}
}
|
以上就是进过改造的实体类。
使用
Observable
数据类型之后数据是通过
get,set
方法来获取和改变的。
在
XML
布局中使用时跟原来的数据类似一样。
集合类型
list,map
这里有两个集合类型
ObservableArrayMap , ObservableArrayList
。使用方法跟原来是
Map,List
是一样的。
添加元素也是通过
add
方法
[Java]
纯文本查看
复制代码
1
2
3
4
5
6
7
|
ObservableArrayMap<String, Object> user =
new
ObservableArrayMap<>();
user.put(
"firstName"
,
"Google"
);
user.put(
"lastName"
,
"Inc."
);
user.put(
"age"
,
17
);
|
主要的区别是在于
XML
布局中的使用。
[XML]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<
data
>
<
import
type
=
"android.databinding.ObservableMap"
/>
<
variable
name
=
"user"
type="ObservableMap<String, Object>"/>
</
data
>
…
<
TextView
android:text
=
'@{user["lastName"]}'
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
<
TextView
android:text
=
'@{String.valueOf(1 + (Integer)user["age"])}'
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
|
以上是官方示例代码
而
List
的区别就是需要定义一个额外的变量作为
list
的
key
[XML]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<
data
>
<
import
type
=
"android.databinding.ObservableList"
/>
<
import
type
=
"com.example.my.app.Fields"
/>
<
variable
name
=
"user"
type="ObservableList<Object>"/>
</
data
>
…
<
TextView
android:text
=
'@{user[Fields.LAST_NAME]}'
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
<
TextView
android:text
=
'@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
|
又是官方代码。
好啦这部分就科普完了。
二、@{String.valueOf(user.age)}这样的表达式支持那些呢?
支持的表达式有:
数学
+ - / * %
字符串连接
+
逻辑
&& ||
二进制
& | ^
一元运算
+ - ! ~
移位
>> >>> <<
比较
== > < >= <=
instanceof
分组
()
null
Cast
方法调用
数据访问
[]
三元运算
?:
例如:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
不支持的:
this
super
new
显式泛型调用
三、点击事件绑定问题
在上面的例子中我是在
Java
代码中进行
button
点击事件的设置的,那么我们能不能在
XMl
布局中进行设置呢?答案是肯定的,实现的方式也非常的简单。
首先在
XML
布局中定义我们的变量作为需要被调用的点击事件
[XML]
纯文本查看
复制代码
1
2
3
4
5
|
<
variable
name
=
"onClick"
type
=
"com.apkbus.mydatabinding.MainActivity"
/>
|
然后在
button
中设置
[XML]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
<
Button
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:text
=
"@{buttonname}"
android:id
=
"@+id/age_button"
android:layout_alignParentBottom
=
"true"
android:layout_alignParentLeft
=
"true"
android:layout_alignParentStart
=
"true"
android:onClick
=
"@{onClick.onClick}"
/>
|
然后看看我们改造好的
Java
代码
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
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
|
public
class
MainActivity
extends
AppCompatActivity {
private
User myUser;
private
ActivityMainBinding myBinding;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
myBinding = DataBindingUtil.setContentView(
this
, R.layout.activity_main);
myUser =
new
User(
"apkbus"
,
4
);
myBinding.setUser(myUser);
myBinding.setButtonname(
"APKBUS快快长大"
);
myBinding.setOnClick(
this
);
}
public
void
onClick(View v){
int
age = myUser.age.get();
myUser.age.set(++age);
}
}
|
之前我们已经知道了,在布局中定义的变量都会自动生成对应的
getXXX,setXXX
方法,所以我们可以很容易的找到跟新变量一样的
set
方法。
四、布局中的高级问题
4.1、import
之前漏了说的一个小知识点,对于布局中我们定义了很多变量的情况下,对于同一个包下的类不需要每次都填写
package
,我们可以跟
Java
中一样
import
。
[XML]
纯文本查看
复制代码
1
|
<
import
type
=
"com.apkbus.mydatabinding.beans.User"
/>
|
然后再定义变量的时候就可以直接这样写啦。
[XML]
纯文本查看
复制代码
1
|
<
variable
name
=
"user"
type
=
"User"
/>
|
当不同的包有一样的类的时候我们可以对冲突的类命名别名
[XML]
纯文本查看
复制代码
1
2
3
4
5
|
<
import
type
=
"android.view.View"
/>
<
import
type
=
"com.example.real.estate.View"
alias
=
"Vista"
/>
|
4.2、自定义binding类名称
默认情况下,
Binding
类的命名是基于所述
layout
文件的名称,用大写开头,除去下划线()以及()后的第一个字母大写,然后添加“
Binding
”后缀。这个类将被放置在一个模块封装包里的
databinding
封装包下。例如,所述
layout
文件
contact_item.xml
将生成
ContactItemBinding
。如果模块包是
com.example.my.app
,那么它将被放置在
com.example.my.app.databinding
。
现在我们可以通过,
data
元素中的
class
属性来自定义
binding
类的名称。
[XML]
纯文本查看
复制代码
1
2
3
4
5
|
<
data
class
=
"MyBinding"
>
...
</
data
>
|
而且还可以自定义
binding
类的包名,如下:
[XML]
纯文本查看
复制代码
1
2
3
4
5
|
<
data
class
=
"com.apkbus.MyBinding"
>
...
</
data
>
|
4.3、布局中有Includes别的布局的时候如何传值呢?
首先添加命名空间
[XML]
纯文本查看
复制代码
1
|
xmlns:bind="http://schemas.android.com/apk/res-auto"
|
然后传递变量
[XML]
纯文本查看
复制代码
1
2
3
|
<
include
layout
=
"@layout/contact"
bind:user
=
"@{user}"
/>
|
注意,这里的
bind:user
为布局
contact
中定义的变量。
以后会以问题的方式不断更新,今天研究就到此了。大家以后遇到问题或者找到某些问题的解决办法也可以在评论区留下你宝贵的经验