1. 程式人生 > >Android官方資料繫結框架DataBinding用法詳解+附帶DEMO原始碼

Android官方資料繫結框架DataBinding用法詳解+附帶DEMO原始碼

今天來了解一下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. classpath "com.android.databinding:dataBinder:1.0-rc1"  

新建module,並且在module的build.gradle檔案中新增

  1. apply plugin: 'com.android.application'  
  2. apply plugin: 'com.android.databinding'  

ok,到現在為止,我們的環境就準備完畢了,下面我們就開始Data Binding的學習啦。

二、Data Binding嘗試 
在程式碼開始,我們並不直接進入新東西的講解,而且以一段程式碼展現Data Binding的魅力。 
首先我們需要一個Java bean,很簡單,一個學生類。

  1. publicclass Student {  
  2.     private String name;  
  3.     private String addr;  
  4.     public Student() {  
  5.     }  
  6.     public Student(String name, String addr) {  
  7.         this.name = name;  
  8.         this.addr = addr;  
  9.     }  
  10.     public String getName() {  
  11.         return name;  
  12.     }  
  13.     publicvoid setName(String name) {  
  14.         this.name = name;  
  15.     }  
  16.     public String getAddr() {  
  17.         returnthis.addr;  
  18.     }  
  19.     publicvoid setAddr(String addr) {  
  20.         this.addr = addr;  
  21.     }  
  22. }  

再來看看我們佈局檔案怎麼寫:

  1. <layoutxmlns:android="http://schemas.android.com/apk/res/android">
  2.     <data>
  3.         <variable
  4.             name="stu"
  5.             type="org.loader.androiddatabinding.Student"/>
  6.     </data>
  7.     <LinearLayout
  8.         android:orientation="vertical"
  9.         android:layout_width="match_parent"
  10.         android:layout_height="wrap_content">
  11.         <TextView
  12.             android:layout_width="wrap_content"
  13.             android:layout_height="wrap_content"
  14.             android:text="@{stu.name}"/>
  15.         <TextView
  16.             android:layout_width="wrap_content"
  17.             android:layout_height="wrap_content"
  18.             android:text="@{stu.addr}"/>
  19.     </LinearLayout>
  20. </layout>

可以看到我們的xml佈局和以前還有有一定的差別的,但是差別也不是很大。 
最後來看看Activity怎麼寫。
  1. publicclass MainActivity extends AppCompatActivity {  
  2.     @Override
  3.     protectedvoid onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);  
  6.         binding.setStu(new Student("loader""山東萊蕪"));  
  7.     }  
  8. }  

Activity的程式碼非常簡單,就添加了兩行程式碼,而且,值得注意的是:我們並沒有findViewById然後再去setText。 
這段小程式碼執行的結果大家可能已經猜到了,就是在介面上顯示loader山東萊蕪兩句話。

)

在看完小例項後,大家是不是感覺棒棒噠? 沒有了之前的find控制元件,沒有了setText,Activity程式碼更加簡潔明瞭! 
下面開始,我們進入Data Binding的學習!

三、 初始Data Binding 
上面的程式碼算是帶領我們進入了Data Binding的世界,那我們先從佈局檔案開始入手Data Binding吧。再來看看上面的佈局檔案。

  1. <layoutxmlns:android="http://schemas.android.com/apk/res/android">
  2.     <data>
  3.         <variable
  4.             name="stu"
  5.             type="org.loader.androiddatabinding.Student"/>
  6.     </data>
  7.     ...  
  8. </layout>

我們的根節點變成了layout,在layout的子節點中分成兩部分,第一部分是data節點,第二部分才是我們之前的根節點,在data節點下我們又定義了一個variable, 
從名稱上看,這應該是一個變數,變數的名稱是stu,型別是org.loader.androiddatabinding.Student,這類似我們在java檔案中這麼定義:
  1. org.loader.androiddatabinding.Student stu;  
ok,這樣很好理解了吧,不過這裡要寫Student完整的包名,一個還好,如果這裡我們需要多個Student呢?要累死? NO,NO,NO,我們還可以向寫java檔案那樣匯入包。
  1. <layoutxmlns:android="http://schemas.android.com/apk/res/android">
  2.     <data>
  3.         <importtype="org.loader.app2.Student"/>
  4.         <variable
  5.             name="stu"
  6.             type="Student"/>
  7.     </data>
  8.     ...  
  9. </layout>

這樣寫,就類似於java的

import org.loader.app2.Student;...Student stu;...

既然變數我們定義好了,那該怎麼使用呢?還是看上面的xml檔案。

  1. <layoutxmlns:android="http://schemas.android.com/apk/res/android">
  2.   ...  
  3.   <LinearLayout
  4.       android:orientation="vertical"
  5.       android:layout_width="match_parent"
  6.       android:layout_height="wrap_content">
  7.       <TextView
  8.           android:layout_width="wrap_content"
  9.           android:layout_height="wrap_content"
  10.           android:text="@{stu.name}"/>
  11.       <TextView
  12.           android:layout_width="wrap_content"
  13.           android:layout_height="wrap_content"
  14.           android:text="@{stu.addr}"/>
  15.   </LinearLayout>
  16. </layout>

恩,注意看兩個TextViewandroid:text,它的值是一個以@開始,以{}包裹的形式出現,而內容呢?是stu.name。stu就是我們上面定義的variable
name還記得嗎?是我們Student類中的一個變數。其實這裡就會去呼叫stu.getName()方法。 
好了,很快,我們就入門了Data Binding,下面讓我們來多定義幾個變數試試看。
  1. <layoutxmlns:android="http://schemas.android.com/apk/res/android">
  2.     <data>
  3.         <importtype="org.loader.app2.Student"/>
  4.         <variable
  5.             name="stu"
  6.             type="Student"/>
  7.         <variable
  8.             name="str"
  9.             type="String"/>
  10.         <variable
  11.             name="error"
  12.             type="boolean"/>
  13.         <variable
  14.             name="num"
  15.             type="int"/>
  16.     </data>
  17.     <LinearLayout
  18.         android:orientation="vertical"
  19.         android:layout_width="match_parent"
  20.         android:layout_height="wrap_content">
  21.         <TextView
  22.             android:layout_width="wrap_content"
  23.             android:layout_height="wrap_content"
  24.             android:text="@{stu.name}"/>
  25.         <TextView
  26.             android:layout_width="wrap_content"
  27.             android:layout_height="wrap_content"
  28.             android:text="@{str}"/>
  29.         <TextView
  30.             android:layout_width="wrap_content"
  31.             android:layout_height="wrap_content"
  32.             android:text="@{String.valueOf(num)}"/>
  33.     </LinearLayout>
  34. </layout>

來看看定義的變數,多了好幾個,有一個String型別的變數我們並沒有導包,這裡說明一下,和在java裡一樣,java.lang包裡的類,我們是可以不用導包的,再往下,一個booleanint型別的變數,都是java基本型別的,所以說嘛,在這裡定義變數,你就想成是在java裡定義就ok。 
再來看看這幾個TextView,第二個,我們直接使用@{str}來為android:text設定成上面定義個str的值,繼續往下要注意了,我們使用了

android:text="@{String.valueOf(num)}"

來設定了一個int型別的變數,大家都知道我們在給android:text設定int型別的值時一定要轉化為String型別,要不它就認為是資原始檔了,這裡我們還學到了一點,在xml中,我們不僅可以使用變數,而且還可以呼叫方法!

四、 變數定義的高階部分 
在上面,我們學會了如何去在xml中定義變數,但是不知道你發現沒?我們沒有定義像ListMap等這樣的集合變數。那到底能不能定義呢?答案肯定是可以的,而且定義的方式和我們上面的基本一致,區別就在於我們還需要為它定義key的變數,例如:

  1. <layoutxmlns:android="http://schemas.android.com/apk/res/android">
  2.     <data>
  3.         <importtype="org.loader.app2.Student"/>
  4.         <importtype="android.graphics.Bitmap"/>
  5.         <importtype="java.util.ArrayList"/>
  6.         <importtype="java.util.HashMap"/>
  7.         <variable
  8.             name="stu"
  9.             type="Student"/>
  10.         <variable
  11.             name="str"
  12.             type="String"/>
  13.         <variable
  14.             name="error"
  15.             type="boolean"/>
  16.         <variable
  17.             name="num"
  18.             type="int"/>
  19.         <variable
  20.             name="list"
  21.             type="ArrayList<String>"/>
  22.         <variable
  23.             name="map"
  24.             type="HashMap<String, String>"/>
  25.         <variable
  26.             name="array"
  27.             type="String[]"/>
  28.         <variable
  29.             name="listKey"
  30.             type="int"/>
  31.         <variable
  32.             name="mapKey"
  33.             type="String"/>
  34.         <variable
  35.             name="arrayKey"
  36.             type="int"/>
  37.     </data>
  38.     <LinearLayout
  39.         android:orientation="vertical"
  40.         android:layout_width="match_parent"
  41.         android:layout_height="wrap_content">
  42.         <TextView
  43.             android:layout_width="wrap_content"
  44.             android:layout_height="wrap_content"
  45.             android:text="@{stu.name}"/>
  46.         <TextView
  47.             android:layout_width="wrap_content"
  48.             android:layout_height="wrap_content"
  49.             android:text="@{str}"/>
  50.         <TextView
  51.             android:layout_width="wrap_content"
  52.             android:layout_height="wrap_content"
  53.             android:text="@{String.valueOf(num)}"/>
  54.         <TextView
  55.             android:layout_width="wrap_content"
  56.             android:layout_height="wrap_content"
  57.             android:text="@{list[listKey]}"/>
  58.         <TextView
  59.             android:layout_width="wrap_content"
  60.             android:layout_height="wrap_content"
  61.             android:text="@{map[`name`]}"/>
  62.         <TextView
  63.             android:layout_width="wrap_content"
  64.             android:layout_height="wrap_content"
  65.             android:text="@{array[0]}"/>
  66.     </LinearLayout>
  67. </layout>

這段程式碼比較長,但是我們僅關心那幾個集合和陣列,可以看到我們定義集合和定義普通變數一樣,只不過這裡我們還指定了一些的泛型,例如:ArrayList&lt;String>。 
下面我們還為下面使用這些集合準備了幾個key,也都是變數。 
繼續看看怎麼使用,和我們在java中使用不同,這裡都是以:集合變數名[key]的形式使用,如果你的key是一個字面字串可以使用反引號,也可以使用轉義後的雙引號。恩,這裡也沒有什麼可以說的了,大家多看幾遍就掌握了,都是概念性的東西,記住就ok。

五、在java程式碼中使用 
前面定義了這麼多變數,但是我們還沒有給他們賦值!在哪賦值呢?肯定是在java程式碼中使用了,大部分情況我們還是在Activity中去使用它,以前我們都是在onCreate方法中通過setContentView去設定佈局,但現在不一樣了,現在我們是用過DataBindingUtil類的一個靜態方法setContentView設定佈局,同時該方法會返回一個物件,什麼物件?這個物件有點特殊,它是一個自動生成的類的物件,看下面:

  1. @Override
  2.    protectedvoid onCreate(Bundle savedInstanceState) {  
  3.        super.onCreate(savedInstanceState);  
  4.        ActivityMainBinding binding = DataBindingUtil.setContentView(this,  
  5.                R.layout.activity_main);  
  6.     }  

看到ActivityMainBinding了嗎?就是它!那自動生成有什麼規則了沒?當然有了,記好了:

將我們佈局檔案的首字母大寫,並且去掉下劃線,將下劃線後面的字母大寫,加上Binding組成。

看看上面的類,是不是符合這個規則。繼續看看這個物件哪來的,是通過

  1. DataBindingUtil.setContentView(this, R.layout.activity_main);  

返回的,DataBindingUtil.setContentView的兩個引數分別是當前Activity和佈局檔案。那接下來,就是我們關心的給變數賦值了。
  1. @Override
  2. protectedvoid onCreate(Bundle savedInstanceState) {  
  3.    ...  
  4.     binding.setStu(new Student("loader"));  
  5.     binding.setStr("string");  
  6.     binding.setError(false);  
  7.     ArrayList<String> list = new ArrayList<String>() {  
  8.         {  
  9.             add("arraylist");  
  10.         }  
  11.     };  
  12.     binding.setList(list);  
  13.     binding.setListKey(0);  
  14.     HashMap<String, String> map = new HashMap<String, String>() {  
  15.         {  
  16.             put("name""hashmap");  
  17.         }  
  18.     };  
  19.     binding.setMap(map);  
  20. //        binding.setMapKey("name");
  21.     String[] array = new String[1];  
  22.     array[0] = "array";  
  23.     binding.setArray(array);  
  24.     binding.setArrayKey(0);  
  25. }  

一連串的binding.setXXX,這個XXX是什麼呢?就是我們在xml中定義的那些變數首字母大寫了!也沒好好說的吧,多看幾遍。

六、 表示式 
短暫的幸福時光,我們還是要告別java程式碼了,繼續回到xml中,這一塊,我們來學習一下表達式,什麼?這玩意在xml中還支援表示式!

  1. <TextView
  2.     android:layout_width="wrap_content"
  3.     android:layout_height="wrap_content"
  4.     android:text='@{error ? "error" : "ok"}'/>

還記得上面我們定義了一個boolean的變數沒有用到,這裡我們就用到了,看好android:text,這裡是一個三元表示式,如果error是true,則text就是error,否則是ok。這裡還支援null合併操作,什麼是null合併,相信看一眼你就知道了
  1. android:text='@{str==null ?? "not null"}'

簡單解釋一下,如果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. <data>
  3.   <importtype="xxx.Name"alias="MyName">
  4.   <importtype="xxx.xx.Name">
  5. </data>
  6. <TextView xxx:@{MyName.getName()}>
  7. <TextView xxx:@{Name.getName()}>
  8. ...  

  1. 自定義Binding名稱 
    還記得系統為我們生成好的那個binding類名嗎?如果只能使用那樣的是不是有點太暴力了?好在google對我們還算友好了,允許我們自定義binding名稱,定製名稱也很簡單,就是給data一個class欄位就ok。 
    例如:
<span
 class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: 
border-box; font-family: 'Source Code Pro', monospace; font-size: 14px; 
line-height: 20px; white-space: pre; background-color: rgba(128, 128, 
128, 0.0470588);"><<span class="hljs-title" style="box-sizing: 
border-box; color: rgb(0, 0, 136);">data</span> <span 
class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0,
 102);">class</span>=<span class="hljs-value" 
style="box-sizing: border-box; color: rgb(0, 136, 
0);">".Custom"</span>></span><span style="color: 
rgb(51, 51, 51); font-family: 'Source Code Pro', monospace; font-size: 
14px; line-height: 20px; white-space: pre; background-color: rgba(128, 
128, 128, 0.0470588);">...</span><span class="hljs-tag" 
style="color: rgb(0, 102, 102); box-sizing: border-box; font-family: 
'Source Code Pro', monospace; font-size: 14px; line-height: 20px; 
white-space: pre; background-color: rgba(128, 128, 128, 
0.0470588);"></<span class="hljs-title" style="box-sizing: 
border-box; color: rgb(0, 0, 
136);">data</span>></span>

那麼:DataBindingUtils.setContentView返回的binding類就是:你的應用包名.Custom

八、事件繫結 
大家都知道,在xml中我們可以給button設定一個onClick來達到事件的繫結,現在DataBinding也提供了事件繫結,而且不僅僅是button。 
來看一下:

  1. <layoutxmlns:android="http://schemas.android.com/apk/res/android">
  2.     <