2015年的google i/o大會釋出了很多新的android庫和工具,data binding就是其中之一。在本系列文章中,我們會探索它的強大之處。
值得注意的是:我寫這篇文章的時候,data binding庫正在測試中,是以很多api可能會在釋出的時候有所改動。
data binding庫提供了一個連結待顯示資料與ui元件的機制,将原本非常被動的資料變成某種形式的資料源。在本篇文章中我們使用一個非常簡單的twitter用戶端作為例子,将twitter api與data binding一起使用。我不會在這裡介紹api以及app設計,我僅僅是使用twitter4j庫抽取了你的twitter首頁上50條按時間排序的資訊,并将他們展示在<code>recyclerview</code>中。所有的代碼都是公開釋出的,你可以放心地使用它了解它。這裡面最有意思的就是其中的資料及它們與view綁定的過程。
我們來看看這篇例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class status {
private final string name;
private final string screenname;
private final string text;
private final string imageurl;
public status(@nonnull string name, @nonnull string screenname, @nonnull string text, @nonnull string imageurl) {
this.name = name;
this.screenname = screenname;
this.text = text;
this.imageurl = imageurl;
}
public string getname() {
return name;
public string getscreenname() {
return screenname;
public string gettext() {
return text;
public string getimageurl() {
return imageurl;
}
這個類維持了幾個需要展示給使用者的基本元素,每一個在<code>recyclerview</code>中的機關都會綁定一個<code>status</code>對象。它裡面的資訊可以從<code>twitter4j.status</code>(由twitter4j的api擷取)中得到,是以我們還要建立一個将<code>twitter4j.status</code>轉化為<code>status</code>的類:
publicclassstatusmarshaller{
publiclist<status>marshall(list<twitter4j.status>statuses){
list<status>newstatuses=newarraylist<>(statuses.size());
for(twitter4j.statusstatus:statuses){
newstatuses.add(marshall(status));
}
returnnewstatuses;
privatestatusmarshall(twitter4j.statusstatus){
useruser=status.getuser();
returnnewstatus(user.getname(),user.getscreenname(),status.gettext(),user.getbiggerprofileimageurl());
這裡沒有用到任何技巧,隻是一個簡單的java操作,跟data binding無關,甚至跟android也無關。
不過需要指出的是,我們本可以直接将view與<code>twitter4j.status</code>綁定在一起,這樣無疑效率會更高。但是data
binding庫使用了mvvm(model-view-viewmodel)設計模式 – model是<code>twitter4j.status</code>,view是ui元件,viewmodel是我們的<code>status</code>對象。viewmodel代表着專門為view設計的一個資料結構,它與view的适配性比model更好。雖然model與viewmodel很像,在目前你可能還感覺不出他們的差別,但是随着我們一步步深入下去,我相信你會明白這樣設計的深意。
接下來我們看一下<code>recyclerview</code>的adapter是怎麼設計的:
32
33
34
35
36
37
38
39
40
41
42
public class statusadapter extends recyclerview.adapter<statusviewholder> {
private final list<status> statuses;
private final statusmarshaller marshaller;
public static statusadapter newinstance() {
list<status> statuses = new arraylist<>();
statusmarshaller marshaller = new statusmarshaller();
return new statusadapter(statuses, marshaller);
statusadapter(list<status> statuses, statusmarshaller marshaller) {
this.statuses = statuses;
this.marshaller = marshaller;
@override
public statusviewholder oncreateviewholder(viewgroup parent, int viewtype) {
context context = parent.getcontext();
layoutinflater inflater = layoutinflater.from(context);
view statuscontainer = inflater.inflate(r.layout.status_item, parent, false);
return new statusviewholder(statuscontainer);
public void onbindviewholder(statusviewholder holder, int position) {
status status = statuses.get(position);
holder.bind(status);
public int getitemcount() {
return statuses.size();
public void setstatuses(list<twitter4j.status> statuses) {
this.statuses.clear();
this.statuses.addall(marshaller.marshall(statuses));
notifydatasetchanged();
終于看到了跟android有關的地方了吧,實際上沒什麼特殊的地方——這隻是一個基本的<code>recyclerview.adapter</code>而已,跟data binding其實關系不大。這裡面唯一跟mvvm有關系的就是在<code>setstatuses()</code>中将<code>twitter4j.status</code>轉換成<code>status</code>。我們馬上将會在statusviewholder中看到怎麼進行資料綁定,首先我們來看看layout是怎麼定義的。
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?xmlversion="1.0"encoding="utf-8"?>
<layoutxmlns:android="http://schemas.android.com/apk/res/android">
<data>
<importtype="android.view.view"/>
<variable
name="status"
type="com.stylingandroid.databinding.data.status"/>
</data>
<relativelayout
android:id="@+id/status_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<imageview
android:id="@+id/status_avatar"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_alignparentleft="true"
android:layout_alignparentstart="true"
android:layout_alignparenttop="true"
android:layout_margin="8dip"
android:contentdescription="@null"/>
<textview
android:id="@+id/status_name"
style="@style/status.name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margintop="8dip"
android:layout_toendof="@id/status_avatar"
android:layout_torightof="@id/status_avatar"
android:text="@{status.name}"/>
android:id="@+id/status_screen_name"
style="@style/status.screenname"
android:layout_alignbaseline="@id/status_name"
android:layout_marginleft="4dip"
android:layout_marginstart="4dip"
android:layout_toendof="@id/status_name"
android:layout_torightof="@id/status_name"
android:text="@{&quot;@&quot;
+ status.screenname}"/>
android:id="@+id/status_text"
style="@style/status.text"
android:layout_width="match_parent"
android:layout_alignleft="@id/status_name"
android:layout_alignparentend="true"
android:layout_alignparentright="true"
android:layout_alignstart="@id/status_name"
android:layout_below="@id/status_name"
android:maxlines="2"
android:singleline="false"
android:text="@{status.text}"/>
</relativelayout>
</layout>
這部分代碼是data binding運作的核心。其中id為<code>status_container</code>的<code>relativelayout</code>是一個普通的layout控件,但是它的父控件<code><layout></code>就不是那麼熟悉了。<code><layout></code>是data
binding庫的一個元件,它含有一個<code><data></code>子元件說明了需要綁定的資料對象(在此處為<code>status</code>),然後我們就可以在此layout中引用該資料對象。
在此布局中textview的<code>android:text</code>屬性值可能跟你正常見到樣子的不大一樣-它們其實都使用了<code>status</code>中的getter。使用<code>@{}</code>包裝說明這是一個data
binding表達式,<code>status.name</code>等同于java中的<code>status.getname()</code>。這是data
binding工作的核心,但是這隻是冰山一角,它還有更多有趣的功能值得我們探索。
你看id為<code>status_screen_name</code>的textview,它的text設定為<code>@{&quot;@&quot; + status.screenname}</code>。你可能看到覺得很困惑,實際上<code>&quot;@&quot;</code>就代表着@,用<code>&quot;</code>将它包裝起來是為了防止@被xml轉義。這個語句告訴我們data
binding語句是很強大的,我們會進一步挖掘它的強大之處。
在定義完layout之後,需要将它和實際資料對象結合,這也是<code>statusviewholder</code>做的事:
publicclassstatusviewholderextendsrecyclerview.viewholder{
privatestatusitembindingbinding;
publicstatusviewholder(viewitemview){
super(itemview);
binding=databindingutil.bind(itemview);
publicvoidbind(statusstatus){
binding.setstatus(status);
它比正常使用的<code>viewholder</code>(通常用于維持子view對象)更簡單一點,在<code>bind()</code>方法中将對象賦予它綁定的layout。首先要注意到我們使用了<code>statusitembinding</code>,可以通過<code>databindingutil.bind()</code>函數擷取到它,這個函數及<code>statusitenbinding</code>類都是data
binding庫生成的。
在下一章中我們會讨論更多的細節,不過此處我們已經有一個基本的應用能夠讓你對data binding的作用有一定認識: