2015年12月24日
[TOC]
1、環(huán)境需要:
1.Android 2.1 (API level 7+)
2.Gradle 1.5.0-alpha1
3.Android Studio 1.3
2、環(huán)境搭建
在build.gradle的android中加入如下字段,等待系統(tǒng)重新編譯。
android{
...
dataBinding {
enabled = true
}
...
}
3、快速開(kāi)始
1、編寫(xiě)布局布局文件
和傳統(tǒng)的布局文件不同,databinding的布局文件的跟標(biāo)簽為layout,他有兩個(gè)子標(biāo)簽,分別為data和原始布局標(biāo)簽,也就是說(shuō)在原來(lái)的布局基礎(chǔ)上多加了一層layout,然后里面又多了一個(gè)data標(biāo)簽。在寫(xiě)完布局文件之后別忘記MakeProject一下,以便系統(tǒng)自動(dòng)生成對(duì)應(yīng)的類(lèi),類(lèi)名為布局文件的名字首字母大寫(xiě)。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 配置變量,name字段為下面想要引用的類(lèi),type為全類(lèi)名,
AS中快捷鍵ctrl+shift+alt+c -->
<data>
<variable
name="person"
type="com.znke.hellodatabinding.Person"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 需要填充的字段用@{}表示 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="25sp"
android:text="@{person.name}"
/>
<!-- 這里要轉(zhuǎn)換一下,不然會(huì)引用int值所對(duì)應(yīng)的R文件中的id -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="25sp"
android:text="@{String.valueOf(person.age)}"
/>
</LinearLayout>
</layout>
2、編寫(xiě)數(shù)據(jù)對(duì)象
一個(gè)簡(jiǎn)單POJO即可
public class Person {
public final String name;
public final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
3、在Acticity中進(jìn)行綁定
在Acticity的onCreate(一個(gè)參數(shù))方法里面進(jìn)行綁定。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//當(dāng)編寫(xiě)完布局文件的時(shí)候,他會(huì)自動(dòng)生成一個(gè)對(duì)應(yīng)的類(lèi),名字為布局文件名稱(chēng)+Binding
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
Person person = new Person("liucl", 22);
//綁定
binding.setPerson(person);
}
*** 注意:***編譯過(guò)中程如果出現(xiàn)xxx.databingding不存在,那么就是你布局文件寫(xiě)錯(cuò)了。仔細(xì)檢查,現(xiàn)在as還不支持語(yǔ)法提示。
運(yùn)行正常如下:

成功了,就是這三步!?。?/em>
4、綁定點(diǎn)擊事件
Databinding支持把事件綁定到對(duì)象中,使用方法和上面大同小異
1、編寫(xiě)事件對(duì)象
public class ClickEvent {
public void click(View v){
Toast.makeText(v.getContext(), "測(cè)試成功 \n Context對(duì)象為"
+ v.getContext().toString()
+ "\n View對(duì)象為"
+ v.toString(),
Toast.LENGTH_SHORT).show();
}
}
在這里我們吐司出v所在Context對(duì)象和View對(duì)象以及他所屬的布局。
2、修改我們的布局文件
首先新建一個(gè)variable引入這個(gè)類(lèi)。在data字段中加入
<variable
name="event"
type="com.znke.hellodatabinding.ClickEvent"/>
其實(shí),我們可以使用import直接導(dǎo)入這個(gè)類(lèi),值得注意的是,這個(gè)區(qū)別于java的import關(guān)鍵字,java中這個(gè)是導(dǎo)入包,而這個(gè)是導(dǎo)入類(lèi)。完整的data代碼如下:
<data>
<import type="com.znke.hellodatabinding.Person"/>
<import type="com.znke.hellodatabinding.ClickEvent"/>
<variable
name="person"
type="Person"/>
<variable
name="event"
type="ClickEvent"/>
</data>
之后,我們?cè)诓季治募屑尤胍粋€(gè)Button,在onClick屬性中引用這個(gè)類(lèi)
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="測(cè)試"
android:onClick="@{event.click}"/>
最后別忘記在Acticity中綁定?。?!
...
binding.setEvent(new ClickEvent());
...
運(yùn)行正常如下:

5、Include對(duì)象傳遞
Databinding支持include對(duì)象傳遞,在A布局里面的對(duì)象可以傳遞到他include進(jìn)來(lái)的布局,
- 在A布局里定義號(hào)自定義命名空間。
xmlns:app="http://schemas.android.com/apk/res-auto"下面要用到。 - A布局里面的include標(biāo)簽
<include layout="@layout/include_click"
app:event="@{event}"/>
- B布局代碼
在B中也要引用一個(gè)傳遞過(guò)來(lái)的類(lèi),這個(gè)布局不用在Acticity中綁定??催^(guò)這個(gè)代碼之后,你可能回想怎么不用merge優(yōu)化呢,——暫時(shí)不支持
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="event"
type="com.znke.hellodatabinding.ClickEvent"/>
</data>
<!-- 暫不支持merge標(biāo)簽 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="測(cè)試"
android:onClick="@{event.click}"/>
</LinearLayout>
</layout>
6、表達(dá)式符號(hào)
布局文件支持這些表達(dá)式,我直接引用谷歌文檔
https://developer.android.com/tools/data-binding/guide.html
Mathematical + - / * %
String + 這個(gè)符號(hào)可能會(huì)引起引號(hào)沖突,需把文字放到資源文件
Logical && ||
Binary & | ^
Unary + - ! ~
Shift >> >>> <<
Comparison == > < >= <=
instanceof
Grouping ()
Literals - character, String, numeric, null
Cast
Method calls
Field access
Array access []
Ternary operator ?:
7、綁定對(duì)象與視圖,使他們能聯(lián)動(dòng)
通過(guò)更改對(duì)象,視圖自動(dòng)就會(huì)隨之改變,這個(gè)功能實(shí)現(xiàn)View與model層邏輯代碼的解耦和。
谷歌給出兩種方法
- Observable Objects
將你的POJO繼承BaseObservable,這個(gè)類(lèi)實(shí)現(xiàn)了Observable??梢詫?shí)現(xiàn)POJO值改變,framework層會(huì)自動(dòng)更新布局文件數(shù)據(jù)
private static class Person extends BaseObservable {
private String name;
private String age;
@Bindable
public String getName() {
return name;
}
@Bindable
public String getAge() {
return age;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
public void setAge(String age) {
this.age = age;
notifyPropertyChanged(BR.age);
}
}
- ObservableFields
其實(shí)他繼承了BaseObservable這個(gè)類(lèi),在他的泛型中添上你的類(lèi)型。同時(shí)
ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble,ObservableParcelable
也都是繼承了BaseObservable。
public class Person {
public ObservableField<String> name = new ObservableField<>();
public ObservableInt age = new ObservableInt();
}
注:谷歌文檔也給了一個(gè)Observable Collections,但我沒(méi)試驗(yàn)成功,但是使用上面那個(gè)方法,是可以實(shí)現(xiàn)集合更改的。
通過(guò)上面的修改后,無(wú)論在那里改這個(gè)對(duì)象里面的值,相應(yīng)的布局文件里面的引用都會(huì)隨之改變。
8、拋棄你的findViewById()
有了Databinding再也不用謝冗長(zhǎng)的findViewById()了,框架會(huì)為你自動(dòng)生成的BR文件。
首先在我們的布局文件中,為我們兩個(gè)TextView加上id,回到Acticity中的Binding對(duì)象,是不是多出了兩個(gè)屬性。
binding.tvText1.setText("");
binding.tvText2.setText("");
這樣你就可以直接給他設(shè)置文本了,但是有一種更好的方法,直接binding.setVariable(com.znke.hellodatabinding.BR.person,new Person());就可以直接給布局文件中的變量賦值。
BR存儲(chǔ)了布局中
data標(biāo)簽中的variable,相當(dāng)于R文件。
9、Attribute Setters(屬性設(shè)置)
這東西就好像Button里面的onClick屬性,然后給他在Acticity里面寫(xiě)一個(gè)對(duì)應(yīng)的實(shí)現(xiàn)方法。他的強(qiáng)大之處就是它可以什么都向里面設(shè)置。不關(guān)是點(diǎn)擊事件。他對(duì)于的實(shí)現(xiàn)方法只需要用一個(gè)注解聲明就可以了,寫(xiě)在那里都可以,即使是一個(gè)沒(méi)有使用的java文件。
通過(guò)一個(gè)Demo來(lái)說(shuō)明,他有一個(gè)輸入框,輸入框輸入文字,上面的TextView也會(huì)隨之改變,這里用到了屬性設(shè)置和對(duì)象綁定。
布局代碼
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="輸入內(nèi)容"
app:textwatcher="@{person}"/>
屬性設(shè)置代碼
public class BindingAdapter {
private static final String TAG = BindingAdapter.class.getSimpleName();
/**
* 添加上這個(gè)注解,方法為靜態(tài)無(wú)返回值,
* 方法有兩個(gè)參數(shù),第一個(gè)是控件本身,第二個(gè)為傳進(jìn)來(lái)的對(duì)象
* 注意這個(gè)方法會(huì)被調(diào)用多次,其中會(huì)有對(duì)象為空的情況。So...
*
* @param e 控件本身
* @param person 傳進(jìn)來(lái)的對(duì)象
*/
@android.databinding.BindingAdapter({"app:textwatcher"})
public static void editTextWatcher(final EditText e,final Person person) {
if (person != null) {
e.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) {
}
@Override
public void afterTextChanged(Editable s) {
person.name.set(s.toString());
e.setSelection(s.length());
}
});
}
}
}
BindingAdapter生命周期所在
在View所在Activity的onCreate方法之后。當(dāng)然,自定義監(jiān)聽(tīng)除外
12-21 11:39:51.289 3388-3388/? E/TAG: onFinishInflate:
12-21 11:39:51.295 3388-3388/? E/TAG: onCreate: null
12-21 11:39:51.331 3388-3388/? E/TAG: initAdapter:
12-21 11:39:51.331 3388-3388/? E/TAG: initManager:
12-21 11:39:51.333 3388-3388/? E/TAG: onAttachedToWindow:
12-21 11:39:51.338 3388-3388/? E/TAG: onMeasure:
12-21 11:39:51.406 3388-3388/? E/TAG: onLayout:
12-21 11:39:51.429 3388-3388/? E/TAG: onMeasure:
12-21 11:39:51.431 3388-3388/? E/TAG: onLayout:
12-21 11:39:51.433 3388-3388/? E/TAG: onDraw:
12-21 11:39:51.502 3388-3388/? E/TAG: onMeasure: