Android数据绑定库Data Binding Library介绍

在安卓巴士上看到了这篇文章,感觉自己有点 落后了,赶紧学习了一下。

转载地址: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

官方文档地址:

https://developer.android.com/intl/zh-cn/tools/data-binding/guide.html

Data Binding Library有什么用?
每当有新东西出现我们都喜欢问有什么用?这可不是然并卵的东西,我们来了解下。

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" ?>
 
< layout xmlns:android = "http://schemas.android.com/apk/res/android" >
 
    < 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
< layout xmlns:android = "http://schemas.android.com/apk/res/android"
 
     >
 
     < 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);
 
             }
 
         });
 
     }
 
  
 
  
 
}

好的,现在我们跑起来看看。

现在已经实现我们的想法了,但是看到这里的代码以及我们实现的过程遇到了几个问题。


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 是一个泛型
我们可以看看他的源码

然后再看看ObservableInt 的源码
其他类型的源码基本类似。这样看来其实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.1import

之前漏了说的一个小知识点,对于布局中我们定义了很多变量的情况下,对于同一个包下的类不需要每次都填写 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]  纯文本查看  复制代码

然后传递变量
[XML]  纯文本查看  复制代码
?
1
2
3
< include layout = "@layout/contact"
 
            bind:user = "@{user}" />

注意,这里的 bind:user 为布局 contact 中定义的变量。
以后会以问题的方式不断更新,今天研究就到此了。大家以后遇到问题或者找到某些问题的解决办法也可以在评论区留下你宝贵的经验


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值