先吐槽下,不說不爽斯碌,不說不通達(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<Book>"></variable>
<variable name="bookMap" type="Map<String,Book>"></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"/>
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個相同的表格出來蒲凶,郁悶啊
表達(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}'
java中的一些特性在DataBinding語法中不支持
- this
- super
- new
- Explicit generic invocation
android:text="@{user.displayName ?? user.lastName}"
就等價于
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
可以在屬性值使用單引號,在表達(dá)式中字符串的值使用雙引號:
android:text='@{map["firstName"]}'
也可以在屬性值使用雙引號旋圆,表達(dá)式中的值字符串的值應(yīng)該使用單引號或者是"`"宠默。
android:text="@{map[`firstName`}"
android:text="@{map['firstName']}"
默認(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>
資源 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 原先的資源引用方式來:
DataBinding 使用技巧
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) {
}
});
上面我們演示的 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();
}
// 我們給 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");
}
}
});
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í)代碼封裝的那部分里。
最后
不要拒絕 findViewById
DataBinding 和 findViewById() 并不是互斥的俊扳,DataBinding的源碼里面也是用到了findViewById()途蒋。如果某些情況真的不適合使用DataBinding,那就用回findViewById吧馋记。xml中的表達(dá)式盡量簡單
xml 文件中不要出現(xiàn)過于復(fù)雜業(yè)務(wù)邏輯号坡,只出現(xiàn)簡單的 UI 相關(guān)的表達(dá)式。不要以為Data Binding是萬能的梯醒,而想盡辦法把邏輯寫在xml中宽堆。往往這個時候你就應(yīng)該將邏輯寫在綁定的ViewModel里面。注意clean
有時候因為修改接口等原因要修改綁定的bean類茸习,這時候偶爾會遇到一些神奇的bug畜隶。不要慌張缩搅,直接clean一下項目再make project一次往往就好了使用BindingConversion注解時需要慎重
原因上面已經(jīng)說了船万。但是它也不失為一個很好用的注解,比方說使用它來進(jìn)行字體的自定義住诸。具體可以參照下面的文章:使用DataBinding來進(jìn)行字體的自定義
kotlin 支持
在 kotlin 中使用猫胁,還要在 gradle.properties 中配置一個參數(shù)
android.databinding.enableV2=true
接下來箱亿,在布局文件當(dāng)中,選中根布局的ViewGroup弃秆,然后按住 「Alt + 回車鍵」 如圖
參考資料
- DataBinding系列(一):DataBinding初認(rèn)識
- DataBinding系列(二):DataBinding的基本用法
- DataBinding系列(三):RecyclerView中使用DataBinding
- DataBinding系列(四):DataBinding進(jìn)階之路
- DataBinding的使用心得
- DataBinding(一)-初識
-
DataBinding(二)-實戰(zhàn)
這里面說了一些 DataBinding 常用的方法届惋,用到了看一下 - 通過Data Binding為RecyclerView打造通用Adapter4
這里有一個 DataBinding 的使用規(guī)范的文章髓帽,不是贊同所有內(nèi)容,但還是推薦大家看看