DataBinding 的簡單使用

先吐槽下,不說不爽斯碌,不說不通達(dá)

不吐不快一死,集合我這幾天學(xué)習(xí) DataBinding 的經(jīng)歷說幾句。DataBinding 這東西也不是 android 的專利傻唾,android 引入這個功能是后知后覺的投慈,一步一趨的跟著別的系統(tǒng)腳步發(fā)展的」诮荆縱觀全局伪煤,14-16年是 android 技術(shù)大爆發(fā)的年頭,各種新技術(shù)層出不窮凛辣,目不暇接抱既,到17年中呢android 技術(shù)的進(jìn)步就停下來了,android 開發(fā)呢也是進(jìn)入生命周期內(nèi)最輝煌的時候扁誓,技術(shù)已經(jīng)非常成熟了防泵,可以遇見的未來,android 的技術(shù)短期內(nèi)沒有什么大的進(jìn)步蝗敢,改變了择克。作為一個普通的 android 開發(fā)者,我們要在這個時間點上努力的吸收之前幾年 android 開發(fā)技術(shù)的精髓前普,和各種優(yōu)秀的思路肚邢,思想,手段和套路拭卿,我覺得這就是我們做 android 開發(fā)應(yīng)該詳細(xì)了解骡湖,舉一反三的,這些也移動開發(fā)的核心精髓峻厚,換個平臺响蕴,換個系統(tǒng),除了基礎(chǔ)的開發(fā)語言和構(gòu)建工具惠桃,和系統(tǒng)知識體系的變化外浦夷,剩下的都是要重復(fù)或者再走 android 這些年這些技術(shù)發(fā)展的老路辖试,我認(rèn)為相同領(lǐng)域技術(shù)思路都是趨同的,區(qū)別是不同平臺劈狐,不同語言的具體實現(xiàn)罷了罐孝。


什么是 DataBinding

什么是 DataBinding 呢,簡單說來就是幫我們實現(xiàn) view 和 data 綁定的工具肥缔,把數(shù)據(jù)映射到 view 的 xml中莲兢,可以在 xml 布局文件中實現(xiàn) view 的賦值,方法調(diào)用续膳。使用 DataBinding 后改艇,我們不同再寫 findViewById,不用再獲取控件對象坟岔,不用再設(shè)置監(jiān)聽谒兄,可以節(jié)省我們 activity 中的很多獲取控件,賦值社付,添加監(jiān)聽所需要的代碼舵变。

DataBinding 是個好東西啊,15年 google IO 大會就開始推了瘦穆,最直接的變化就是催生了 android 中 MVVM 的出現(xiàn)纪隙,MVVM = MVP + DataBinding 。一線公司早就普及 MVVM 了扛或,大伙作為一個普普通通的 andoid 開發(fā)者绵咱,一定要身上時代,什么是時代熙兔,大廠就是時代悲伶,大廠都在干什么,我們就干什么住涉,不求跟上大廠麸锉,但求不落后太多,所以小伙伴們走起舆声,MVVM 作為 MVP 的進(jìn)階花沉,我們一定要學(xué)好,這期中的重中之重 DataBinding 一定不要落下媳握,其實沒多難碱屁,DataBinding 初步學(xué)習(xí)半天就可以,其中涉及到列表和多module的部分是比較復(fù)雜 的蛾找,我們多看幾個開源的 app 例子就行娩脾,這里我也盡量詳細(xì)的說一說。經(jīng)驗是干什么的打毛,就是帶著我們少走彎路柿赊,快速學(xué)習(xí)的俩功,有的可以快,有的不能快碰声,必須去體會诡蜓,恰巧 DataBinding 就是可以快起來的部分。


DataBinding 的初步使用

寫了一段時間的博客后奥邮,我是深深體會到了万牺,一不論多復(fù)雜的事一定要按步驟拆解成一段段簡單的步奏罗珍,這樣才能學(xué)的快洽腺,學(xué)的明白,中間斷掉了覆旱,之后也好撿起來繼續(xù)學(xué)蘸朋。

千里之行,始于足下扣唱,所以呢藕坯,我們先來把 DataBinding 集成進(jìn)來,做個最簡單的實現(xiàn)噪沙。

先導(dǎo)入 DataBinding 功能

因為 DataBinding 是 google 強推的嘛炼彪,所以 1.5版以上 gradle 就自帶 DataBinding 庫了,只要我們在 gradle 配置中打開 DataBinding 這個功能就可以了正歼,不用再引入遠(yuǎn)程依賴了辐马。哪個 module 需要就在哪個 module 的 build.gradle 編譯配置文件中聲明啟動 dataBinding 就可以了。注意 Gradle 最低要 1.5 alpha 版局义。

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

這里我在 app 這個 module 里面聲明的喜爷。DataBinding 在 gradle 中的表現(xiàn)形式就是 DataBinding 相關(guān)的 task 編譯任務(wù)了,我們 enabled = true 之后萄唇,在編譯時就會執(zhí)行 DataBinding 的 task 了檩帐。

在 xml 中使用
<?xml version="1.0" encoding="utf-8"?> 
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    
    <data>
        <import type="com.bloodcrown.bwdatabindingdemo.Book"/>
        <variable name="book" type="Book"></variable>
    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{book.name}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_editor_absoluteY="20dp"
            tools:text="name"/>

        <TextView
            android:id="@+id/tv_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@{book.price}"
            app:layout_constraintLeft_toLeftOf="@id/tv_name"
            app:layout_constraintTop_toBottomOf="@id/tv_name"
            tools:text="price"/>

    </android.support.constraint.ConstraintLayout>
</layout>

注意 dataBinding 在 xml 中的使用規(guī)則:

  • 使用 DataBinding 必須要使用 <layout> 這個標(biāo)簽作為 xml 的根部局
  • 所有 DataBinding 的內(nèi)容都寫在 <data >標(biāo)簽內(nèi)
  • <import > 是導(dǎo)入所使用的對象的類型
  • <variable > 是具體聲明一個數(shù)據(jù)對象,<name> 是這個對象的名字另萤,<type> 是這個對象的類型湃密,<variable > 可以有多個
  • 使用 @{ } / @{book.price} 的方式可以把這個聲明出來的數(shù)據(jù)對象的莫個數(shù)據(jù)設(shè)置給我們的 view
java 代碼的使用部分
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        Book book = new Book("AAA", "35");
        mainBinding.setBook(book);
    }
}
  • 首先我們使用 DataBinding 的方式替換我們的 setContentView,使用 DataBindingUtil 這個類的 setContentView 方法四敞,里面?zhèn)魅隺ctivity 和 layout 布局
  • 然后 DataBinding 的 setContentView 方法會根據(jù)類名給我們返回一個 DataBinding 的輔助類勾缭,這里就是這個 ActivityMainBinding 了。名字規(guī)則是 Activity 類型名在前 + 類中其他的名字目养,這個 ActivityMainBinding 輔助類我們不用編譯 AS 也會自動幫我們生成俩由,但是呢要是你在這里有問題,那就手動編譯一下癌蚁。
  • 我們拿到輔助類之后幻梯,new 一個數(shù)據(jù)對象出來兜畸,然后把這個數(shù)據(jù)對象設(shè)置給輔助類 ActivityMainBinding,因為我們在 xml 中的<data>中聲明幾個數(shù)據(jù)對象碘梢,那我們在 activity onCreate 中就得給 DataBinding 的輔助類 ActivityMainBinding咬摇,設(shè)置幾個數(shù)據(jù)對象進(jìn)去,然后 DataBinding 才可以根據(jù)我們設(shè)置進(jìn)來的數(shù)據(jù)對象煞躬,給相關(guān) view 設(shè)值肛鹏。

DataBinding 的數(shù)據(jù)更新

view 在本質(zhì)的職責(zé)是反應(yīng)數(shù)據(jù)的變化,那么我們就來說說在 DataBinding 中我們怎么更新數(shù)據(jù)恩沛。DataBinding 的數(shù)據(jù)本質(zhì)是我餓們在 xml 中 <data> 標(biāo)簽中聲明的數(shù)據(jù)對象在扰,我們在java 代碼中把相關(guān)的數(shù)據(jù)對象設(shè)置給 DataBinding 的輔助類,那么我們有一下幾種方式:

  • 整體再設(shè)置一次數(shù)據(jù) bean 對象雷客,這個適合更新整體數(shù)據(jù)
  • 使用 BaseObservable 芒珠,操作數(shù)據(jù) bean 的 set 方法更新字段數(shù)據(jù),適合更新部分?jǐn)?shù)據(jù)
  • 使用 ObservableFields 搅裙,只更新有需求的數(shù)據(jù)字段皱卓,這個不夠靈活
  • 使用 DataBinding 也有的集合類型:ObservableArrayMap , ObservableArrayList
  • 雙向數(shù)據(jù)綁定
更新整體數(shù)據(jù)

這個本質(zhì)上就是重復(fù)一次我們給 DataBinding 輔助類設(shè)置數(shù)據(jù)的過程,使用很簡單

 Book book = new Book("BBBB", "65");
 mainBinding.setBook(book);

這樣就可以了部逮,說實話娜汁,我更傾向于這樣方式,因為大多數(shù)時候兄朋,數(shù)據(jù)都是休要整體更新的

使用 BaseObservable

BaseObservable 是一個基類掐禁,需要我們的數(shù)據(jù) bean 繼承這個基類,然后給屬性的 get 方法
添加@Bindable 這個注解蜈漓,然后在屬性的 set 方法中添加上 DataBinding 更新某個字段的方法

public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    } 

這樣做呢穆桂,其實是在 DataBinding 的輔助類中把相關(guān)屬性的更新和 view 的賦值方法關(guān)聯(lián)在一起,完整的數(shù)據(jù) bean 如下:

public class Book extends BaseObservable {

    public String name;
    public String price;

    public Book() {
    }

    public Book(String name, String price) {
        this.name = name;
        this.price = price;
    }

    @Bindable
    public String getName() {
        return name;
    }

   public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
        notifyPropertyChanged(BR.price);
    }
}
在 activity 中操作數(shù)據(jù) bean 的 set 方法就可以同步把數(shù)據(jù)更新到 view 中了
mBook.setName("CCC");

有個更好的解釋:

Obserable接口有一個自動添加和移除監(jiān)聽器的機制融虽,但是通知數(shù)據(jù)更新取決于開發(fā)者享完。為了使開發(fā)變得簡單,谷歌創(chuàng)建了BaseObserable這個基礎(chǔ)類來集成監(jiān)聽器注冊機制有额。通過給getter方法添加Bindable注解般又,通知setter方法。

使用 ObservableFields

ObservableFields 是一個對屬性添加 DataBinding 更新功能的代理類巍佑,針對不同的數(shù)據(jù)類型有不同類型的 ObservableFields :ObservableBoolean茴迁、 ObservableByte ObservableChar、ObservableShort萤衰、ObservableInt堕义、ObservableLong、ObservableFloat脆栋、ObservableDouble倦卖、 ObservableParcelable 等洒擦。

這種方式不是主流類型,使用不便怕膛,不能擴(kuò)展屬性熟嫩,所以這里我簡單的放個例子:

// 數(shù)據(jù) bean 中聲明一個屬性
 public ObservableField<String> age = new ObservableField<>(); 

// activity 中更新數(shù)據(jù)
 mBook.age.set("ABABA");
ObservableArrayMap , ObservableArrayList

這2個集合類型是 DataBinding 為了方便數(shù)據(jù)刷新提供的,不用我們再手動通知集合的變化褐捻,只要我們更新了集合的某些數(shù)據(jù)掸茅,就能自動更新相關(guān)的 view 的數(shù)據(jù)

簡單舉個例子:

// 聲明一個集合對象
private ObservableArrayMap<String, Object> mapUser = new ObservableArrayMap<>();

// 這樣直接更新集合數(shù)據(jù)就可以了
mapUser.put("firstName", "zhu");
mapUser.put("lastName", "chen");
mapUser.put("age", 27);
雙向數(shù)據(jù)綁定

雙向數(shù)據(jù)綁定就是當(dāng) view 改變時,data 會跟著改變柠逞;data 改變時昧狮,view 會跟著改變,核心就是給 view 指定 :@={book.name}边苹,這里用 EditText 做個例子

  <EditText
            android:text="@={book.name}"
/>

這樣當(dāng)輸入不同的內(nèi)容時陵且,數(shù)據(jù)就會同步更新到綁定的 book 這個數(shù)據(jù)對象中裁僧。


DataBinding 的更豐富使用

上文書書說 DataBinding 的精髓都在布局的 xml 文件中个束,其中有著豐富的操作,DataBinding 在xml 有越多的用法聊疲,那么就會越多的替代我們 java 中的代碼茬底,越多的減少 activity 的代碼量,而且使用 DataBinding 后获洲,頁面的賦值邏輯在 xml 也會顯得剛加清晰阱表,可見。那么現(xiàn)在我們就來看看 DataBinding 都有那些玩法贡珊。

1. variable 標(biāo)簽支持的數(shù)據(jù)類型

java 能使用的 variable 標(biāo)簽當(dāng)然都能

  • 基本數(shù)據(jù)類型最爬,我們按照 java 的寫法即可,比如 String门岔,int
  • 若是引入了名字相同的類爱致,可以給類添加別名
  • 引用類型使用 import 引入全包名即可
  • list,map 結(jié)合類型同引用類型 import 導(dǎo)下包名就行
// 引用類型
<import type="com.bloodcrown.bwdatabindingdemo.Book"/>
<variable name="book" type="Book"></variable>

android:text="@{book.name}"

------------------------------------

// 基本數(shù)據(jù)類型
// 基本數(shù)據(jù)類型不用 import 導(dǎo)入寒随,直接在 type 類型里寫就行qw糠悯,寫 int 可能會報錯,忽略就行妻往,寫相應(yīng)的包裝類型就得引入包了
<variable name="name" type="String"></variable>
<variable name="price" type="int"></variable>

// 在使用 int 等基本數(shù)據(jù)類型時互艾,注意轉(zhuǎn)成字符串再賦值,DataBinding 不會幫我們做類型轉(zhuǎn)換的
android:text="@{String.valueOf(price)}"

------------------------------------

// 集合數(shù)據(jù)類型
// 集合的使用方式包括 [] 和 get 2種方式
<import type="java.util.List"/>
<import type="java.util.Map"/>
<variable name="bookList" type="List&lt;Book&gt;"></variable>
<variable name="bookMap" type="Map&lt;String,Book&gt;"></variable>

android:text="@{bookList.get(1).name}"  
android:text="@{bookList[1].price}"  
android:text='@{bookMap["111"].name}'
android:text='@{bookMap.get("111").price}'
// 注意其中特殊特好的使用讯泣,集合枚舉的 <> 符號直接寫xml 不認(rèn)纫普,需要用轉(zhuǎn)移符號,寫 map 時好渠,key 要是 String 的昨稼,那么你可以再里面用 " " ,但是這行的 xml 外面就得用 ' ' 才行溉箕,這點注意啊,要不 xml 總是報錯

------------------------------------

// 設(shè)置別名
// 我們引入的不同包的類可能重名悦昵,那么自然我們需要加一個別名來加以區(qū)分了肴茄,用alias表示
<import type="com.zx.databindingdemo.bean.UserBean" />
<import type="com.zx.databindingdemo.bean.user.UserBean" alias="UserBean2"/>

<variable  name="user"  type="UserBean" />
<variable  name="user2"  type="UserBean2"/>

常用的轉(zhuǎn)義字符表

2. 如何調(diào)用,注冊各種方法

大家想啊但指,既然在 xml 中都可以直接操作屬性值了寡痰,那么我調(diào)用一個方法還不是妥妥的啊。這里要區(qū)分方法的調(diào)用和監(jiān)聽方法的注冊

對于一個 button 的點擊事件來說棋凳,我們可以走下面2中方式:

  • DataBinding 會根據(jù) id 名拦坠,生成相應(yīng)的 view 對象,然后我們給這個 view 設(shè)置監(jiān)聽
 mainBinding.btnTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText( MainActivity.this,"AAA",Toast.LENGTH_SHORT ).show();
            }
        });
  • 在 xml 中聲明一個點擊事件的對象剩岳,然后設(shè)置進(jìn) android:onClick 屬性里
// 先聲明點擊事件對象
 <import type="android.view.View.OnClickListener"/>
 <variable name="testClick" type="OnClickListener"></variable>

// 再使用
 <Button  android:onClick="@{testClick}"  />

// activity 里填充這個點擊事件對象進(jìn)去
mainBinding.setTestClick(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText( MainActivity.this,"BBB",Toast.LENGTH_SHORT ).show();
            }
        });

總體感覺和原來的注冊方式?jīng)]啥區(qū)別

除了各種事件的注冊外贞滨,我們在利用 DataBinding 可以在 XML 中使用各種方法,靜態(tài)方法拍棕,和對象中的方法都是可以直接調(diào)用的晓铆,注意是在給 view 屬性賦值時可以直接調(diào)用

// Utils 里面有一個靜態(tài)方法

// 導(dǎo)入包含靜態(tài)方法的類,DataBinding 中要想使用任何類型绰播,除了基本數(shù)據(jù)類型骄噪,都得導(dǎo)包
 <import type="com.bloodcrown.bwdatabindingdemo.Utils"/>
<import type="com.bloodcrown.bwdatabindingdemo.MainPersenter"/>
<variable name="persenter" type="MainPersenter"></variable>

// 方法直接使用即可,直接傳參也是可以的
 <Button
            android:text="@{Utils.getName(book.name)}"  

 <Button
            android:text="@{  android:text="@{persenter.getPrice()}"}"  
3. DataBinding 對 lambda 表達(dá)式的支持

DataBinding 支持我們直接在 xml 書寫 lambda 表達(dá)式蠢箩,常用的就是注冊 click 點擊事件了链蕊,在 view 的 onClick 屬性中我們需要傳入一個對象,通過上面的內(nèi)容學(xué)習(xí)谬泌,我們是聲明了一個 ClickListener 類型的對象數(shù)據(jù)出來滔韵,然后在 java 中傳入這個對象的方式做的。但是我們在這里可以直接實現(xiàn) lambda 表達(dá)式書寫一個匿名實現(xiàn)類對象出來掌实,這樣就省了我們在 java 中傳入對象的代碼了陪蜻。這里說一下 lambda 表達(dá)式就是對匿名實現(xiàn)類對象的簡寫,所以我們雖然看著像是調(diào)用了方法的樣子潮峦,其實我們是寫了一個匿名實現(xiàn)類對象出來囱皿,本質(zhì)上還是傳入了一個對象。不熟悉 lambda 表達(dá)式的看這里:Lambda表達(dá)式以及AS 對其的支持 忱嘹,還是推薦大家去學(xué)習(xí)一下 Lambda的嘱腥,現(xiàn)在各大語言都在往這種函數(shù)式編程上靠,像 lambda 表達(dá)式這種從函數(shù)式編程上借鑒過來的東西拘悦,以后只會越來越多的齿兔,抗拒這種變化是不明智的。

大家可能會這么寫 Lambda 表達(dá)式

 <Button
            android:onClick="@{() -> persenter.speak(book.name)}"

但是很遺憾,你這么寫會報錯分苇,一個類型轉(zhuǎn)換異常的錯誤添诉,知道為什么嗎?這個還是要從 Lambda表達(dá)式說起医寿。Lambda 的特征是隱藏實現(xiàn)類的 類名方法名栏赴,因為 java 8可以知道從上下文(方法中對于參數(shù)的限定)知道類的類型,并且規(guī)定了類的里面只能有一個方法靖秩,那么這里我們使用的就是這個方法:

 public void onClick(View v) {
                ......
           }

注意我們使用 Lambda 重寫的就是這個 onClick 方法须眷,onClick 方法要求傳入一個 view 的參數(shù)的,上面我們沒有傳這個參數(shù)沟突,所以報錯了花颗。那么我們把 Lambda 表達(dá)式修改一下:

 <Button
            android:onClick="@{(view) -> persenter.speak(book.name)}"

view 表示點擊事件的 view 對象,這樣 Lambda 就可以跑了惠拭。不過呢扩劝,使用 Lambda 寫點擊事件對象是屬于動態(tài)類型的,因為每點擊一次都會從心生成一個 點擊的 匿名實現(xiàn)類 設(shè)置 view职辅,所以注意這個動態(tài)的特性棒呛,合理利用,因為這樣點擊事件的數(shù)據(jù)也是可以使用新的了罐农。

4. 其他一些監(jiān)聽器
除了 onClick 之外条霜,還提供了一些特定的點擊事件催什,這里需要注意涵亏,下面幾個我沒用到過,也是從別人那里摘過來的:

Class Listener Setter Attribute
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

這里不知道為啥出現(xiàn)2個相同的表格出來蒲凶,郁悶啊

5. 支持表達(dá)式語言

表達(dá)式語言和java語法很相似气筋。以下是相同的:

  • Mathematical: + - / * %
  • String concatenation +
  • Logical && ||
  • Binary & | ^
  • Unary + - ! ~
  • Shift >> >>> <<
  • Comparison == > < >= <=
  • instanceof
  • Grouping ()
  • Literals - character, String, numeric, null
  • Cast
  • Method calls
  • Field access
  • Array access []
  • Ternary operator ?:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

6. 不支持的操作

java中的一些特性在DataBinding語法中不支持

  • this
  • super
  • new
  • Explicit generic invocation

7. Null Coalescing 空運算符

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

就等價于

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

8. 關(guān)于字符串的符號再次說明一下

可以在屬性值使用單引號,在表達(dá)式中字符串的值使用雙引號:

android:text='@{map["firstName"]}'

也可以在屬性值使用雙引號旋圆,表達(dá)式中的值字符串的值應(yīng)該使用單引號或者是"`"宠默。

android:text="@{map[`firstName`}"
android:text="@{map['firstName']}"

9. 自定義 DataBinding 輔助類類名

默認(rèn)情況下,binding 類的名稱取決于布局文件的命名灵巧,以大寫字母開頭搀矫,移除下劃線,后續(xù)字母大寫并追加 “Binding” 結(jié)尾刻肄。這個類會被放置在 databinding 包中瓤球。舉個例子,布局文件 contact_item.xml 會生成 ContactItemBinding 類敏弃。如果 module 包名為 com.example.my.app 卦羡,binding 類會被放在 com.example.my.app.databinding 中。

通過修改 data 標(biāo)簽中的 class 屬性,可以修改 Binding 類的命名與位置绿饵。

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

以上會在 databinding 包中生成名為 CustomBinding 的 binding 類欠肾。如果需要放置在不同的包下,可以在前面加 “.”

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

這樣的話拟赊, CustomBinding 會直接生成在 module 包下刺桃。如果提供完整的包名,binding 類可以放置在任何包名中

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

10. DataBinding 中使用資源文件

資源 id 方面我們同傳統(tǒng)方式去寫就行吸祟,但是還是稍有些差別的虏肾。不如看這個例子:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

運行起來,可能有的版本會報錯誤欢搜,因為這是 DataBinding 的 bug封豪,有人說修了,有人說還有炒瘟,碰上的朋友這樣改

android:padding="@{large? (int)@dimen/largePadding : (int)@dimen/smallPadding}"

因為 DataBinding 生成數(shù)據(jù)的數(shù)據(jù)格式可能和我們實際需要的不同吹埠,注意這點。

其他的例子我們舉一個疮装,替換 String.xml 中的占位符:

// 先聲明字符串資源
<string name="nameFormat">Full Name: %1$s:%2$s</string>

// DataBinding 在 xml 中可以直接引用使用
android:text="@{@string/nameFormat(firstName, lastName)}"

DataBinding 對于資源的文件頭命名可能和 android 傳統(tǒng)的有些地方不一樣缘琅,下面的表是官方文檔上的,找資料的話都是這張表廓推,沒有其他的資料刷袍,大家實際要到問題可以參考這樣表,但是優(yōu)先還是按照 android 原先的資源引用方式來:


簡書這幾天寫列表有問題樊展,截圖頂一下

11. DataBinding 中使用資源文件


DataBinding 使用技巧

1. 對 include 的支持

xml 布局中不可避免的要使用 include 標(biāo)簽呻纹,那么 DataBinding 怎么兼容這個 include 標(biāo)簽?zāi)亍F鋵嵵灰?外層的 xml 把 include 聲明的 DataBinding 數(shù)據(jù)對象傳給 include 就行专缠,但是要注意 DataBinding 不支持 include 的 merge 標(biāo)簽

include 的布局 name.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="user"
            type="com.liangfeizc.databinding.model.User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

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

</layout>

總體的布局文件

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <import type="com.liangfeizc.databinding.model.User" />
        <variable
            name="user"
            type="User" />
        <variable
            name="listener"
            type="com.liangfeizc.databinding.listener.OkListener" />
        <variable
            name="okText"
            type="String" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <include
            android:id="@+id/layout_input"
            layout="@layout/layout_input" />

        <include
            layout="@layout/user"
            app:user="@{user}" />

        <include
            layout="@layout/layout_btn_ok"
            app:okText="@{okText}"
            app:listener="@{listener}"/>
    </LinearLayout>
</layout>

另一個 include 的 xml ,layout_input.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <EditText
            android:id="@+id/et_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>

</layout>

注意啊雷酪,如何調(diào) include 布局的某一個組件呢

  binding.layoutInput.etName.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                User user = new User(s.toString(), "Liang");

                binding.setUser(user);
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

2. 對 fragment 的支持

上面我們演示的 activity,是通過 DataBinding 通過 setContentView 方法實現(xiàn) DataBinding 和 activity xml 布局實現(xiàn)綁定的涝婉,那么問題來了 fragment 怎么辦

activity 我們這么寫

private ActivityDemoBinding mBinding;
protected void onCreate(Bundle savedInstanceState) {
    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_demo);
 }

Fragment 我們這么寫:

private FragmentHomeBinding mBinding;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
    mBinding = DataBindingUtil.inflate(inflater, R.layout.homepage_fragment, container, false);
    return mBinding.getRoot();
}

3. 對 ViewStub 的支持

// 我們給 ViewStub 設(shè)置初始化函數(shù)
 mainBinding.bookStub.setOnInflateListener(new ViewStub.OnInflateListener() {
            @Override
            public void onInflate(ViewStub stub, View inflated) {
                bookStubBinding = DataBindingUtil.bind(inflated);
                bookStubBinding.setBook(mBook);
            }
        });

// 模擬一個點擊事件加載 ViewStub 
 mainBinding.setTestClick(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mainBinding.bookStub.isInflated()) {
                    mainBinding.bookStub.getViewStub().inflate();
                } else {
                    mBook.setName("stub");
                    mBook.setPrice("888");
                }
            }
        });

4. DataBinding 中的注解

DataBinding 可以使用的注解:

  • @BindingMethod :修改view 某個方法名字
  • @BindingAdapter : 給 view 添加set/get 方法哥力,相當(dāng)于添加自定義屬性
  • @BindingConversion : 提供數(shù)據(jù)類型轉(zhuǎn)換方法

@BindingMethod 用的不多,這里就不說了墩弯,我們來看下 @BindingAdapter 這個注解吩跋,可以給 view 添加自定義屬性,相當(dāng)?shù)暮糜冒∮婀ぃ覀儾挥迷俣x復(fù)雜的 view 自定義屬性了锌钮,@BindingMethod 書寫簡單,功能強大涨缚,xml 中的自頂提示很友好轧粟。

先定義給 view 設(shè)置這個自定義屬性的具體執(zhí)行方法

public class TextBingUtils {

    @BindingAdapter("info")
    public static void setInfo(TextView view, String info) {
        view.setText(info);
    }

}

注意 @BindingAdapter 注解里面的參數(shù)就是這個自定義屬性的名字策治,在 xml 里 app:xx 就可以使用了。另外這個方法寫在哪里都沒事兰吟,但是方法必須是靜態(tài)的通惫,DataBinding 框架在編譯時會自動檢索 @BindingAdapter 這個注解的所有方法。

然后在就可以在 xml 中使用了

 <TextView
            app:info='@{"info"}'
/>

@BindingConversion 用的不多混蔼,但是也得說一下履腋,這個注解會給 DataBinding 提供默認(rèn)的2個數(shù)據(jù)類型之間的轉(zhuǎn)換方法,可以放置一些類型轉(zhuǎn)換錯誤惭嚣,寫法上和 @BindingAdapter 相同遵湖。但是使用這轉(zhuǎn)換器屬性時我們必須要小心,因為DataBinding是不懂得區(qū)分是否真的需要使用整個轉(zhuǎn)換器的晚吞。比方說我們創(chuàng)建兩個相同類型的轉(zhuǎn)換方法延旧,DataBinding 就只會使用第一個

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
    return new ColorDrawable(color);
}
<View
    android:background="@{isError.get() ? @color/red : @color/white}"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_height="@{height}" />

DataBinding 對 RecyclerView 的優(yōu)化

為啥要單開一章說呢,因為的確是太重要了槽地,DataBinding 對 RecyclerView 支持真的是一大亮點啊迁沫,直接的就是使用 DataBinding ,我們就不用再寫 Viewholder 了捌蚊。Viewholder 的工作就是 findviewbyid 持有相應(yīng)的 view 的引用好讓我們來設(shè)置數(shù)據(jù)集畅,DataBinding 會自動根據(jù) view 的 id 生成相關(guān)的對象,至少從這點出來缅糟,DataBinding 對我們都是非常有意義的了挺智。好了,來看看在 RecyclerView 中使用 DataBinding 的基礎(chǔ)方式窗宦。

新的 ViewHolder 書寫方式赦颇,每個 ViewHolder 只需要持有相應(yīng) view 對應(yīng)的 DataBinding 輔助類對象,通過他恩那個找到所有的子 view 引用迫摔。

public class BookBindingViewHolder extends RecyclerView.ViewHolder {

    private ItemListBinding t;

    public BookBindingViewHolder(ItemListBinding t) {
        super(t.getRoot());
        this.t = t;
    }

    public ItemListBinding getBinding() {
        return t;
    }

    public void setT(ItemListBinding t) {
        this.t = t;
    }
}

item 的布局文件沐扳,要相生成 DataBinding 輔助類,必須在 xml 中顯示使用 DataBinding

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
>

    <data>
        <import type="com.bloodcrown.bwdatabindingdemo.Book"/>
        <variable name="book" type="Book"></variable>
    </data>

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="10dp"
        android:text="@{book.name}"
        android:textColor="@color/colorPrimary"
        android:textSize="22sp"
    />
</layout>

新的 adapter 通過 DataBinding 類刷新數(shù)據(jù)

public class BookBindingAdapter extends RecyclerView.Adapter<BookBindingViewHolder> {

    private List<Book> data;

    public BookBindingAdapter(List<Book> data) {
        this.data = data;
    }

    @Override
    public BookBindingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new BookBindingViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_list, parent, false));
    }

    @Override
    public void onBindViewHolder(BookBindingViewHolder holder, int position) {
        Log.d("AAA", "position:" + position);
        holder.getBinding().setBook(data.get(position));
        holder.getBinding().setVariable(BR.book, data.get(position));
        holder.getBinding().executePendingBindings();
    }

    @Override
    public int getItemCount() {
        return data == null ? 0 : data.size();
    }

    public void setData(List<Book> data) {
        this.data = data;
    }
}

holder.getBinding().executePendingBindings() 這句話是刷新界面句占,否則可能會出現(xiàn)這個問題: RecyclerView使用databinding出現(xiàn)數(shù)據(jù)閃爍問題

另外注意列表在更新數(shù)據(jù)時也可以使用 holder.getBinding().setVariable(BR.book, data.get(position)); 這個方法是更新xml 里面定義的數(shù)據(jù)對象的,BR.book 是個 int 值躯嫉,指向xml 中聲明的數(shù)據(jù)對象的id地址

當(dāng)然上面這是最簡單的 DataBinding 列表實現(xiàn)纱烘,甚至多 itemType 都不支持,下面優(yōu)化下多 itemType 的問題

// 寫一個返回 itemType 的接口祈餐,然后數(shù)據(jù) bean 實現(xiàn)這個接口

public interface IBaseBindingAdapterItemProvide {
    int getItemType();
}

// 然后處理一下 adapter 擂啥,能夠兼容多類型的 item

public class CatBindingAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<IBaseBindingAdapterItemProvide> data;

    public CatBindingAdapter(List<IBaseBindingAdapterItemProvide> data) {
        this.data = data;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == R.layout.item_list) {
            return new BookBindingViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_list, parent, false));
        }
        if (viewType == R.layout.item_cat) {
            return new CatBindingViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_cat, parent, false));
        }
        return new BookBindingViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_list, parent, false));
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        if (holder instanceof BookBindingViewHolder) {
            Book book = (Book) data.get(position);
            ((BookBindingViewHolder) holder).getBinding().setBook(book);
            ((BookBindingViewHolder) holder).getBinding().executePendingBindings();
            return;
        }

        if (holder instanceof CatBindingViewHolder) {
            Cat cat = (Cat) data.get(position);
            ((CatBindingViewHolder) holder).getBinding().setCat(cat);
            ((CatBindingViewHolder) holder).getBinding().executePendingBindings();
        }
    }

    @Override
    public int getItemCount() {
        return data == null ? 0 : data.size();
    }

    @Override
    public int getItemViewType(int position) {
        return data.get(position).getItemType();
    }

    public void setData(List<IBaseBindingAdapterItemProvide> data) {
        this.data = data;
    }
}

恩,現(xiàn)在可以支持多類型的列表了帆阳,但是要是我們在實際開發(fā)中對這段原始代碼不經(jīng)任何雕琢(封裝哺壶,設(shè)計),那么說明我們做開發(fā)真的是沒長進(jìn)。能夠初步封裝根據(jù)效果可以認(rèn)為是初中級水平山宾,能夠?qū)懛庋b出一個易于擴(kuò)展的優(yōu)秀的庫出來至扰,可以視為高級水平了。期望大家多多用心资锰,在封裝自己的庫時敢课,是水平提升最快的時候了,知識點的學(xué)習(xí)是偏記憶绷杜,理解直秆,考研智商和記憶能力。那么封裝原始代碼為庫就是考研我們的基礎(chǔ)代碼水平了鞭盟,設(shè)計到的都是硬知識點圾结,也是最難以提升的部分,需要大毅力才行齿诉,但也是最重要的代碼技能了疫稿。

本文先于篇幅,對于用 DataBinding 來優(yōu)化 RecyclerView 就寫到這里了鹃两,更多更優(yōu)秀的內(nèi)容我會開單章的遗座,放到通過 MVP 學(xué)習(xí)代碼封裝的那部分里。


最后

  1. 不要拒絕 findViewById
    DataBinding 和 findViewById() 并不是互斥的俊扳,DataBinding的源碼里面也是用到了findViewById()途蒋。如果某些情況真的不適合使用DataBinding,那就用回findViewById吧馋记。

  2. xml中的表達(dá)式盡量簡單
    xml 文件中不要出現(xiàn)過于復(fù)雜業(yè)務(wù)邏輯号坡,只出現(xiàn)簡單的 UI 相關(guān)的表達(dá)式。不要以為Data Binding是萬能的梯醒,而想盡辦法把邏輯寫在xml中宽堆。往往這個時候你就應(yīng)該將邏輯寫在綁定的ViewModel里面。

  3. 注意clean
    有時候因為修改接口等原因要修改綁定的bean類茸习,這時候偶爾會遇到一些神奇的bug畜隶。不要慌張缩搅,直接clean一下項目再make project一次往往就好了

  4. 使用BindingConversion注解時需要慎重
    原因上面已經(jīng)說了船万。但是它也不失為一個很好用的注解,比方說使用它來進(jìn)行字體的自定義住诸。具體可以參照下面的文章:使用DataBinding來進(jìn)行字體的自定義


kotlin 支持

在 kotlin 中使用猫胁,還要在 gradle.properties 中配置一個參數(shù)

android.databinding.enableV2=true

接下來箱亿,在布局文件當(dāng)中,選中根布局的ViewGroup弃秆,然后按住 「Alt + 回車鍵」 如圖


16abb5f5b0765477.png

參考資料

這里有一個 DataBinding 的使用規(guī)范的文章髓帽,不是贊同所有內(nèi)容,但還是推薦大家看看

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脑豹,一起剝皮案震驚了整個濱河市郑藏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晨缴,老刑警劉巖译秦,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異击碗,居然都是意外死亡筑悴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門稍途,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阁吝,“玉大人,你說我怎么就攤上這事械拍⊥挥拢” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵坷虑,是天一觀的道長甲馋。 經(jīng)常有香客問我,道長迄损,這世上最難降的妖魔是什么定躏? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮芹敌,結(jié)果婚禮上痊远,老公的妹妹穿的比我還像新娘。我一直安慰自己氏捞,他們只是感情好碧聪,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著液茎,像睡著了一般逞姿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上豁护,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天哼凯,我揣著相機與錄音,去河邊找鬼楚里。 笑死,一個胖子當(dāng)著我的面吹牛猎贴,可吹牛的內(nèi)容都是我干的班缎。 我是一名探鬼主播蝴光,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼达址!你這毒婦竟也來了蔑祟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤沉唠,失蹤者是張志新(化名)和其女友劉穎疆虚,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體满葛,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡径簿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嘀韧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片篇亭。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖锄贷,靈堂內(nèi)的尸體忽然破棺而出译蒂,到底是詐尸還是另有隱情,我是刑警寧澤谊却,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布柔昼,位于F島的核電站,受9級特大地震影響炎辨,放射性物質(zhì)發(fā)生泄漏捕透。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一蹦魔、第九天 我趴在偏房一處隱蔽的房頂上張望激率。 院中可真熱鬧,春花似錦勿决、人聲如沸乒躺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘉冒。三九已至,卻和暖如春咆繁,著一層夾襖步出監(jiān)牢的瞬間讳推,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工玩般, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留银觅,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓坏为,卻偏偏與公主長得像究驴,于是被迫代替她去往敵國和親镊绪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345