Android數據綁定庫Data Binding Library介紹

在安卓巴士上看到了這篇文章,感覺自己有點 落後了,趕緊學習了一下。

轉載地址:http://www.apkbus.com/forum.php?mod=viewthread&tid=245694&extra=&_dsign=29ec4049


2015年的Google I/O 大會上發佈了三個重要的支持庫:

1Material 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的速度

4UI與功能的解耦合


獲取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.1API 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);
 
}

好啦,又是官方示例代碼。在activityonCreate方法中進行了基本的數據綁定。一句一句代碼給你們解釋。
[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);
 
            }
 
        });
 
    }
 
  
 
  
 
}

好的,現在我們跑起來看看。

現在已經實現我們的想法了,但是看到這裏的代碼以及我們實現的過程遇到了幾個問題。


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的區別就是需要定義一個額外的變量作爲listkey
[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中定義的變量。
以後會以問題的方式不斷更新,今天研究就到此了。大家以後遇到問題或者找到某些問題的解決辦法也可以在評論區留下你寶貴的經驗


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章