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
中新增以下依賴
- classpath "com.android.databinding:dataBinder:1.0-rc1"
新建module
,並且在module
的build.gradle檔案中新增
- apply plugin: 'com.android.application'
- apply plugin: 'com.android.databinding'
ok,到現在為止,我們的環境就準備完畢了,下面我們就開始Data Binding的學習啦。
二、Data Binding嘗試
在程式碼開始,我們並不直接進入新東西的講解,而且以一段程式碼展現Data Binding的魅力。
首先我們需要一個Java
bean
,很簡單,一個學生類。
- publicclass Student {
- private String name;
- private String addr;
- public Student() {
- }
- public Student(String name, String addr) {
- this.name = name;
- this.addr = addr;
- }
- public String getName() {
- return name;
- }
- publicvoid setName(String name) {
- this.name = name;
- }
- public String getAddr() {
- returnthis.addr;
- }
- publicvoid setAddr(String addr) {
- this.addr = addr;
- }
- }
再來看看我們佈局檔案怎麼寫:
- <layoutxmlns:android="http://schemas.android.com/apk/res/android">
- <data>
- <variable
- name="stu"
- type="org.loader.androiddatabinding.Student"/>
- </data>
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{stu.name}"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{stu.addr}"/>
- </LinearLayout>
- </layout>
可以看到我們的xml佈局和以前還有有一定的差別的,但是差別也不是很大。
最後來看看
Activity
怎麼寫。- publicclass MainActivity extends AppCompatActivity {
- @Override
- protectedvoid onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
- binding.setStu(new Student("loader", "山東萊蕪"));
- }
- }
Activity
的程式碼非常簡單,就添加了兩行程式碼,而且,值得注意的是:我們並沒有findViewById
然後再去setText
。
這段小程式碼執行的結果大家可能已經猜到了,就是在介面上顯示loader
和山東萊蕪
兩句話。
)
在看完小例項後,大家是不是感覺棒棒噠? 沒有了之前的find控制元件,沒有了setText,Activity
程式碼更加簡潔明瞭!
下面開始,我們進入Data Binding的學習!
三、 初始Data Binding
上面的程式碼算是帶領我們進入了Data Binding的世界,那我們先從佈局檔案開始入手Data Binding吧。再來看看上面的佈局檔案。
- <layoutxmlns:android="http://schemas.android.com/apk/res/android">
- <data>
- <variable
- name="stu"
- type="org.loader.androiddatabinding.Student"/>
- </data>
- ...
- </layout>
我們的根節點變成了
layout
,在layout
的子節點中分成兩部分,第一部分是data
節點,第二部分才是我們之前的根節點,在data
節點下我們又定義了一個variable
, 從名稱上看,這應該是一個變數,變數的名稱是
stu
,型別是org.loader.androiddatabinding.Student
,這類似我們在java檔案中這麼定義:- org.loader.androiddatabinding.Student stu;
Student
完整的包名,一個還好,如果這裡我們需要多個Student
呢?要累死?
NO,NO,NO,我們還可以向寫java檔案那樣匯入包。
- <layoutxmlns:android="http://schemas.android.com/apk/res/android">
- <data>
- <importtype="org.loader.app2.Student"/>
- <variable
- name="stu"
- type="Student"/>
- </data>
- ...
- </layout>
這樣寫,就類似於java的
import org.loader.app2.Student;...Student stu;...
既然變數我們定義好了,那該怎麼使用呢?還是看上面的xml檔案。
- <layoutxmlns:android="http://schemas.android.com/apk/res/android">
- ...
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{stu.name}"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{stu.addr}"/>
- </LinearLayout>
- </layout>
恩,注意看兩個
TextView
的android:text
,它的值是一個以@
開始,以{}包裹的形式出現,而內容呢?是stu.name
。stu就是我們上面定義的variable
, name還記得嗎?是我們
Student
類中的一個變數。其實這裡就會去呼叫stu.getName()
方法。 好了,很快,我們就入門了Data Binding,下面讓我們來多定義幾個變數試試看。
- <layoutxmlns:android="http://schemas.android.com/apk/res/android">
- <data>
- <importtype="org.loader.app2.Student"/>
- <variable
- name="stu"
- type="Student"/>
- <variable
- name="str"
- type="String"/>
- <variable
- name="error"
- type="boolean"/>
- <variable
- name="num"
- type="int"/>
- </data>
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{stu.name}"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{str}"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{String.valueOf(num)}"/>
- </LinearLayout>
- </layout>
來看看定義的變數,多了好幾個,有一個
String
型別的變數我們並沒有導包,這裡說明一下,和在java裡一樣,java.lang
包裡的類,我們是可以不用導包的,再往下,一個boolean
和int
型別的變數,都是java基本型別的,所以說嘛,在這裡定義變數,你就想成是在java裡定義就ok。 再來看看這幾個
TextView
,第二個,我們直接使用@{str}
來為android:text
設定成上面定義個str
的值,繼續往下要注意了,我們使用了
android:text="@{String.valueOf(num)}"
來設定了一個int
型別的變數,大家都知道我們在給android:text
設定int
型別的值時一定要轉化為String
型別,要不它就認為是資原始檔了,這裡我們還學到了一點,在xml中,我們不僅可以使用變數,而且還可以呼叫方法!
四、 變數定義的高階部分
在上面,我們學會了如何去在xml中定義變數,但是不知道你發現沒?我們沒有定義像List
、Map
等這樣的集合變數。那到底能不能定義呢?答案肯定是可以的,而且定義的方式和我們上面的基本一致,區別就在於我們還需要為它定義key的變數,例如:
- <layoutxmlns:android="http://schemas.android.com/apk/res/android">
- <data>
- <importtype="org.loader.app2.Student"/>
- <importtype="android.graphics.Bitmap"/>
- <importtype="java.util.ArrayList"/>
- <importtype="java.util.HashMap"/>
- <variable
- name="stu"
- type="Student"/>
- <variable
- name="str"
- type="String"/>
- <variable
- name="error"
- type="boolean"/>
- <variable
- name="num"
- type="int"/>
- <variable
- name="list"
- type="ArrayList<String>"/>
- <variable
- name="map"
- type="HashMap<String, String>"/>
- <variable
- name="array"
- type="String[]"/>
- <variable
- name="listKey"
- type="int"/>
- <variable
- name="mapKey"
- type="String"/>
- <variable
- name="arrayKey"
- type="int"/>
- </data>
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{stu.name}"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{str}"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{String.valueOf(num)}"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{list[listKey]}"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{map[`name`]}"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{array[0]}"/>
- </LinearLayout>
- </layout>
這段程式碼比較長,但是我們僅關心那幾個集合和陣列,可以看到我們定義集合和定義普通變數一樣,只不過這裡我們還指定了一些的泛型,例如:ArrayList<String>
。
下面我們還為下面使用這些集合準備了幾個key,也都是變數。
繼續看看怎麼使用,和我們在java中使用不同,這裡都是以:集合變數名[key]的形式使用,如果你的key是一個字面字串可以使用反引號,也可以使用轉義後的雙引號。恩,這裡也沒有什麼可以說的了,大家多看幾遍就掌握了,都是概念性的東西,記住就ok。
五、在java程式碼中使用
前面定義了這麼多變數,但是我們還沒有給他們賦值!在哪賦值呢?肯定是在java程式碼中使用了,大部分情況我們還是在Activity
中去使用它,以前我們都是在onCreate
方法中通過setContentView
去設定佈局,但現在不一樣了,現在我們是用過DataBindingUtil
類的一個靜態方法setContentView
設定佈局,同時該方法會返回一個物件,什麼物件?這個物件有點特殊,它是一個自動生成的類的物件,看下面:
- @Override
- protectedvoid onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ActivityMainBinding binding = DataBindingUtil.setContentView(this,
- R.layout.activity_main);
- }
看到
ActivityMainBinding
了嗎?就是它!那自動生成有什麼規則了沒?當然有了,記好了:
將我們佈局檔案的首字母大寫,並且去掉下劃線,將下劃線後面的字母大寫,加上Binding組成。
看看上面的類,是不是符合這個規則。繼續看看這個物件哪來的,是通過
- DataBindingUtil.setContentView(this, R.layout.activity_main);
返回的,DataBindingUtil.setContentView的兩個引數分別是當前
Activity
和佈局檔案。那接下來,就是我們關心的給變數賦值了。
- @Override
- protectedvoid onCreate(Bundle savedInstanceState) {
- ...
- binding.setStu(new Student("loader"));
- binding.setStr("string");
- binding.setError(false);
- ArrayList<String> list = new ArrayList<String>() {
- {
- add("arraylist");
- }
- };
- binding.setList(list);
- binding.setListKey(0);
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("name", "hashmap");
- }
- };
- binding.setMap(map);
- // binding.setMapKey("name");
- String[] array = new String[1];
- array[0] = "array";
- binding.setArray(array);
- binding.setArrayKey(0);
- }
一連串的binding.setXXX,這個XXX是什麼呢?就是我們在xml中定義的那些變數首字母大寫了!也沒好好說的吧,多看幾遍。
六、 表示式
短暫的幸福時光,我們還是要告別java程式碼了,繼續回到xml中,這一塊,我們來學習一下表達式,什麼?這玩意在xml中還支援表示式!
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text='@{error ? "error" : "ok"}'/>
還記得上面我們定義了一個boolean的變數沒有用到,這裡我們就用到了,看好
android:text
,這裡是一個三元表示式,如果error是true,則text就是error,否則是ok。這裡還支援null合併操作,什麼是null合併,相信看一眼你就知道了
- 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了兩個相同名稱的類咋辦?別怕,別名來拯救你!例如:
- ...
- <data>
- <importtype="xxx.Name"alias="MyName">
- <importtype="xxx.xx.Name">
- </data>
- <TextView xxx:@{MyName.getName()}>
- <TextView xxx:@{Name.getName()}>
- ...
- 自定義Binding名稱
還記得系統為我們生成好的那個binding類名嗎?如果只能使用那樣的是不是有點太暴力了?好在google對我們還算友好了,允許我們自定義binding名稱,定製名稱也很簡單,就是給data一個class欄位就ok。
例如:
那麼:DataBindingUtils.setContentView返回的binding類就是:你的應用包名.Custom
。
八、事件繫結
大家都知道,在xml中我們可以給button
設定一個onClick
來達到事件的繫結,現在DataBinding也提供了事件繫結,而且不僅僅是button
。
來看一下:
- <layoutxmlns:android="http://schemas.android.com/apk/res/android">
- <