DataBinding使用全面詳解

一、DataBinding使用

1.使用環(huán)境

DataBinding是一個(gè)support library,所以它可以支持所有的android sdk拦宣,最低可以到android2.1(API7)。
使用DataBinding需要Android Gradle插件的支持肥哎,版本至少在1.5以上秉版,需要的Android studio的版本在1.3以上。

在Android Studio上使用飞蛹,需要在module級(jí)別的build.gradle上添加對(duì)DataBinding的支持:

android {
    ....
    dataBinding {
        enabled = true
    }
}

如果是在library中使用,那么使用使用該library的module也需要在build.gradle添加灸眼。

2.xml布局文件數(shù)據(jù)綁定

DataBinding的layout files和普通的非DataBinding布局文件是有一些區(qū)別的卧檐,下面是一個(gè)基礎(chǔ)的使用了DataBinding的布局文件:

<?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>

變量user作為被綁定的數(shù)據(jù),在layout文件中是這樣描述和使用的:

<variable name="user" type="com.example.User"/>

layout中view的屬性值通過"@{}"這樣的語法表達(dá)方式和數(shù)據(jù)user實(shí)現(xiàn)綁定焰宣,本例中將TextView的text值設(shè)置為user對(duì)象的fisrtName了:

<TextView android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@{user.firstName}"/>

3.定義數(shù)據(jù)綁定的Data對(duì)象

Data對(duì)象官方文檔中POJO類和Java Bean都可以霉囚,這里我建議使用如下Java Bean:

    public class User {
     private String firstName;
      private 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;
     }
    }

Note:但是定義一個(gè)如上的數(shù)據(jù),并不能滿足刷新UI的要求匕积,我們需要的Data 還得是一個(gè)Observable Data盈罐。

DataBinding中有三種不同的數(shù)據(jù)榜跌,object、field盅粪、collection钓葫。

Observable Objects

Observable是提供添加移除監(jiān)聽的一個(gè)java接口,DataBinding基于此接口提供了一個(gè)基礎(chǔ)類BaseObserable,我們可以這樣使用它湾揽,通過Bindale注解綁定一個(gè)getter瓤逼,當(dāng)data屬性發(fā)生改變?cè)趕etter中發(fā)出通知笼吟,這樣就實(shí)現(xiàn)了響應(yīng)

private static class User extends BaseObservable {
   private String firstName;
   private String lastName;
   @Bindable
   public String getFirstName() {
       return this.firstName;
   }
   @Bindable
   public String getLastName() {
       return this.lastName;
   }
   public void setFirstName(String firstName) {
       this.firstName = firstName;
       notifyPropertyChanged(BR.firstName);
   }
   public void setLastName(String lastName) {
       this.lastName = lastName;
       notifyPropertyChanged(BR.lastName);
   }
}

ObseravbleField

google為我們提供了一些Obserable類: ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, and ObservableParcelable库物。

public static class User {
   public final ObservableField<String> firstName =
       new ObservableField<>();
   public final ObservableField<String> lastName =
       new ObservableField<>();
   public final ObservableInt age = new ObservableInt();
}

ObseravbleCollection

** ObservableArrayMap **

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);

在xml中使用:

<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"/>

ObservableArrayList

ObservableArrayList<Object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);

xml使用:

<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"/>

4.綁定數(shù)據(jù)

Android studio會(huì)根據(jù)layout文件自動(dòng)生成一個(gè)默認(rèn)的Binding類,類名是根據(jù)layout文件名生成的贷帮,并有"Binding"后綴結(jié)束戚揭。例如:activity_main.xml生成的Binding類為ActivityMainBinding,可用如下方式使用Binding類:

@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);
}

啟動(dòng)程序撵枢,將會(huì)看到user的數(shù)據(jù)已經(jīng)在ui中顯示了民晒,或者我們也可以這樣實(shí)現(xiàn):

MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());

在ListView或者RecyclerView的adpater中item里使用DataBinding

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

5.事件處理

DataBinding允許我們?cè)趚ml中view的一些事件屬性(如onClick等)中填寫DataBinding表達(dá)式,也可以通過綁定listener的方式去實(shí)現(xiàn)锄禽。歸納起來就是:
方法引用和監(jiān)聽綁定潜必,下面介紹著兩種方式:

方法引用

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.Handlers"/>
       <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}"
           android:onClick="@{handlers::onClickFriend}"/>
   </LinearLayout>
</layout>

監(jiān)聽綁定

public class Presenter {
    public void onSaveClick(Task task){}
}

布局文件

  <?xml version="1.0" encoding="utf-8"?>
  <layout xmlns:android="http://schemas.android.com/apk/res/android">
      <data>
          <variable name="task" type="com.android.example.Task" />
          <variable name="presenter" type="com.android.example.Presenter" />
      </data>
      <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent">
          <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
          android:onClick="@{() -> presenter.onSaveClick(task)}" />
      </LinearLayout>
  </layout>

6.imports

導(dǎo)入類:

<data>
    <import type="android.view.View"/>
</data>

現(xiàn)在可以在xml中使用View類的靜態(tài)資源:

<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

也可以為導(dǎo)入類,重新定義一個(gè)別名:

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
        alias="Vista"/>

下面是個(gè)集合的例子:

<data>
    <import type="com.example.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List<User>"/>
</data>

7.自定義Binding類類名

生成當(dāng)前目錄下的ContactItemBinding

<data class="ContactItem">
    ...
</data>

也可以通過下面的方式指定生成類存放的目錄

<data class="com.example.ContactItem">
...
</data>

8.表達(dá)式

支持的運(yùn)算符:
<ul>
<li>數(shù)學(xué)運(yùn)算符: + - / * %</li>
<li>字符串拼接: +</li>
<li>邏輯運(yùn)算符: && ||</li>
<li>二進(jìn)制: & | ^</li>
<li>一元運(yùn)算符: +</li>
<li>位運(yùn)算符: >> >>> <<</li>
<li>比較: == > < >= <=</li>
<li>instanceof</li>
<li>()</li>
<li>數(shù)據(jù)類型: character, String, numeric, null</li>
<li>類型轉(zhuǎn)換(ClassCast)</li>
<li>方法回調(diào)(Method calls)</li>
<li>數(shù)據(jù)屬性</li>
<li>數(shù)組:[]</li>
<li>三元操作符:沃但?</li>
</ul>

列如:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

一些在java中常用而DataBinding xml中不支持的:

<ul>
<li>this
<li>super
<li>new
<li>泛型
</ul>
一個(gè)比較有意思的“??”操作符:

android:text="@{user.displayName ?? user.lastName}"

它等于:

android:text="@{user.displayName != null ? user.displayName : user.lastName}"

9.Binding類的其他生成方式

前面磁滚,我們提到了一個(gè)獲取Binding類的方法,我們還可以這樣

MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);

或者:

MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

二宵晚、DataBinding高級(jí)使用

1.動(dòng)態(tài)變量

有時(shí)候我們可能不知道Binding類的名稱垂攘,比如RecyclerView.Adapter中item布局可能有很多,并不會(huì)對(duì)應(yīng)特定的Binding類淤刃,但是仍然需要通過** onBindViewHolder(VH, int)**去綁定數(shù)據(jù)晒他,下面的列子是,所有的子布局都有一個(gè)"item"變量逸贾,通過ViewDataBinding基類去完成綁定:

public void onBindViewHolder(BindingHolder holder, int position) {
   final T item = mItems.get(position);
   holder.getBinding().setVariable(BR.item, item);
   holder.getBinding().executePendingBindings();
}

Immediate Binding

當(dāng)一個(gè)變量被綁定或者綁定的對(duì)象發(fā)生變化是陨仅,DataBinding會(huì)讓這些改變排隊(duì)去在下一幀刷險(xiǎn)之前改變,有些時(shí)候binding效果必須立刻執(zhí)行铝侵,這時(shí)候可以使用executePendingBindings()灼伤。

Background Thread
只要綁定數(shù)據(jù)不是一個(gè)collection,我們可以在非ui主線程去改變數(shù)據(jù)哟沫,不會(huì)有任何線程切換問題饺蔑,DataBinding會(huì)自動(dòng)處理。

2.Attribute Setters

當(dāng)一個(gè)被綁定的數(shù)據(jù)的值發(fā)生改變時(shí)嗜诀,Binding類會(huì)自動(dòng)尋找該view上的綁定表達(dá)式上的方法去改變view猾警,通過google數(shù)據(jù)綁定框架我們可以去自定義這些方法孔祸。

對(duì)于一個(gè)xml的attribute,data binding會(huì)去尋找setAttribute方法发皿,xml屬性的命名空間是沒有關(guān)系的崔慧。比如TextView上的一個(gè)屬性android:text,會(huì)去尋找setText(String)穴墅。如果表達(dá)式返回的是int則會(huì)去尋找setText(int)惶室,所以必須確保xml中表達(dá)式返回正確的數(shù)據(jù)類型,必要時(shí)需要數(shù)據(jù)轉(zhuǎn)換玄货。我們可以比較容易地為任何屬性創(chuàng)造出setter去使用dataBinding皇钞。比如support包下的DrawerLayout沒有任何屬性,但是確有很多setter松捉,下面利用這些已有的setter中的一個(gè):

<android.support.v4.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}"/>

自定義setters

一些xml屬性需要自己去定義并實(shí)現(xiàn)邏輯夹界,比如android:paddingLeft。但是setPadding(left,top,right,bottom)是存在的隘世,那么我們可以同BindingAdapter注解去自定義個(gè)自己的setter:

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
   view.setPadding(padding,
                   view.getPaddingTop(),
                   view.getPaddingRight(),
                   view.getPaddingBottom());
}

Note:開發(fā)者自定義的BindingAdapter和android自帶的發(fā)生沖突時(shí)可柿,data bingding會(huì)優(yōu)先采用開發(fā)者自定義的。

多參數(shù)的BindingAdapter

@BindingAdapter({"bind:imageUrl", "bind:error"})
public static void loadImage(ImageView view, String url, Drawable error) {
   Picasso.with(view.getContext()).load(url).error(error).into(view);
}

BindingAdpater方法可以對(duì)屬性的舊值和新值進(jìn)行處理

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
   if (oldPadding != newPadding) {
       view.setPadding(newPadding,
                       view.getPaddingTop(),
                       view.getPaddingRight(),
                       view.getPaddingBottom());
   }
}

事件處理的列子

@BindingAdapter("android:onLayoutChange")
public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
       View.OnLayoutChangeListener newValue) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        if (oldValue != null) {
            view.removeOnLayoutChangeListener(oldValue);
        }
        if (newValue != null) {
            view.addOnLayoutChangeListener(newValue);
        }
    }
}

3.雙向綁定

在xml屬性上使用語法"{@=}",
使用該方法就是雙向綁定了

     <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}"/>
     </LinearLayout>

Note:需要注意的是丙者,使用該語法必須要要反向綁定的方法复斥,android原生view都是自帶的,所以使用原生控件無須擔(dān)心械媒,但是自定義view的話需要我們通過InverseBindingAdapter注解類實(shí)現(xiàn)目锭,下面是個(gè)例子

     @InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
     public static String captureTextValue(TextView view, CharSequence originalValue) {
                 CharSequence newValue = view.getText();
              CharSequence oldValue = value.get();
                 if (oldValue == null) {
                     value.set(newValue);
                 } else if (!contentEquals(newValue, oldValue)) {
                     value.set(newValue);
                 }
             }

4.Converters

有時(shí)候我們想這樣寫xml屬性

    <View
   android:background="@{isError ? @color/red : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

但是xml屬性的setter是一個(gè)drawable,我們可以通過BindingConversion實(shí)現(xiàn)

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
   return new ColorDrawable(color);
}

由于這個(gè)注解至是發(fā)生在setter層面上滥沫,所以并不支持下面的混合寫法

<View
   android:background="@{isError ? @drawable/error : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

三侣集、關(guān)于DataBinding的一些個(gè)人看法

1.DataBinding使用心得

使用xml進(jìn)行view布局
采用符合Java Bean規(guī)范的數(shù)據(jù)原型
規(guī)范的自定義View
禁止在BindingAdapter中的setter方法中改變數(shù)據(jù)或者做數(shù)據(jù)處理
不建議用BindingConversion處理數(shù)據(jù)轉(zhuǎn)換
不建議在xml布局中處理view事件
不建議在xml中使用復(fù)雜的表達(dá)式

2.DataBinding使用的一些思考

DataBinding的不足之處:

1.DataBinding在xml提供了豐富的操作符,但是由于Android studio天生的xml語法檢查的貧弱兰绣,xml布局中的表達(dá)式邏輯錯(cuò)誤世分,不能準(zhǔn)確定位,導(dǎo)致debug難度增加缀辩,事實(shí)上一些BindingAdapter的錯(cuò)誤在build的時(shí)候也會(huì)被提示xml錯(cuò)誤臭埋。

2.對(duì)自定義view的要求比較高,需要自定義綁定方法臀玄,如BindingAdapter等瓢阴。

3.可能由于java 8移除apt,采用了新的API的緣故健无,所以即使Android Studio2.2已經(jīng)開始支持java 8特性荣恐,但是需要開啟jack編譯鏈,DataBinding與之沖突,導(dǎo)致在代碼中不能使用lambda表達(dá)式等java 8特性叠穆。值得欣慰的是少漆,這一問題將在Android Studio2.4中得到解決。

PS:數(shù)據(jù)綁定的應(yīng)用軟件開發(fā)的一種趨勢(shì)硼被,使用DataBinding的優(yōu)點(diǎn)顯而易見示损,但是使用的時(shí)候我們也需要小心。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嚷硫,一起剝皮案震驚了整個(gè)濱河市检访,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仔掸,老刑警劉巖脆贵,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異嘉汰,居然都是意外死亡丹禀,警方通過查閱死者的電腦和手機(jī)状勤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門鞋怀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人持搜,你說我怎么就攤上這事密似。” “怎么了葫盼?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵残腌,是天一觀的道長。 經(jīng)常有香客問我贫导,道長抛猫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任孩灯,我火速辦了婚禮闺金,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘峰档。我一直安慰自己败匹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布讥巡。 她就那樣靜靜地躺著掀亩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪欢顷。 梳的紋絲不亂的頭發(fā)上槽棍,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼炼七。 笑死外里,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的特石。 我是一名探鬼主播盅蝗,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼姆蘸!你這毒婦竟也來了墩莫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤逞敷,失蹤者是張志新(化名)和其女友劉穎狂秦,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體推捐,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裂问,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了牛柒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堪簿。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖皮壁,靈堂內(nèi)的尸體忽然破棺而出椭更,到底是詐尸還是另有隱情,我是刑警寧澤蛾魄,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布虑瀑,位于F島的核電站,受9級(jí)特大地震影響滴须,放射性物質(zhì)發(fā)生泄漏舌狗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一扔水、第九天 我趴在偏房一處隱蔽的房頂上張望痛侍。 院中可真熱鬧,春花似錦铭污、人聲如沸恋日。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岂膳。三九已至,卻和暖如春磅网,著一層夾襖步出監(jiān)牢的瞬間谈截,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留簸喂,地道東北人毙死。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像喻鳄,于是被迫代替她去往敵國和親扼倘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容