引子
前一段開始學習并在項目中運用 MVVM 時,首先學習的便是 DataBinding 這個 Google 爸爸官方出品的數據綁定框架。我在網上查資料時發現,DataBinding 相關的資料雖然不少,但大多是在 DataBinding 推出不久時寫的,有很多之后新加入的功能都沒有提到,內容略顯陳舊,而 Android 開發者官網提供的官方文檔也不并全面,有些很重要的知識點沒有提到。所以我想結合官方文檔和自己的實際使用經驗,向大家介紹一下 DataBinding 的使用方法。在這個系列完成后,我想再聊聊 RxJava、Retrofit、DataBinding 等相結合打造 MVVM 架構的方法。
如果你在閱讀時發現錯誤,或者有什么建議,歡迎反饋給我。
DataBinding 框架簡介
DataBinding 是 Google 在2015年7月推出的一個支持庫,最低兼容至 Android 2.1(API level 7+),需要 Gradle 版本高于 1.5.0-alpha1。
Google 一直在增強 DataBinding 的功能以及 Android Studio 對其的支持度,目前 Studio 已經支持了大部分的代碼補全和錯誤提示,但仍有部分正確語法會被報錯。Anyway,我強烈建議你保持 Android Studio 和 Gradle 為最新穩定版。
配置環境
首先確保你已下載安裝最新版 Support Repository
,接下來在 module
層級的 build.gradle
文件中添加以下配置代碼:
android {
...
dataBinding {
enabled = true
}
}
綁定數據
首先創建一個標準的 JavaBean
:
// Person.class
public class Person {
private String name;
// 省略的 getter 和 setter 方法
}
然后需要編寫布局文件。將根元素改為 layout
,在其中添加用于包裹數據的 data
元素和正常的布局代碼。
一個編寫好的布局類似如下代碼:
// activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="person"
type="com.spacerover.databindingdemo.data.Person"/>
</data>
<LinearLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/nameTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{person.name}"
android:textAlignment="center"/>
<EditText
android:id="@+id/nameEt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{person.name}"/>
</LinearLayout>
</layout>
代碼中的 variable
用于定義數據綁定所需要的變量,其中 name
屬性定義變量名,type
定義變量類型。
示例中將 person
聲明為位于 com.spacerover.databindingdemo.data
包下的 Person
,也就是我們剛剛寫的 JavaBean
。
使用 @{}
語法可以將變量綁定到控件的屬性。
接下來需要編寫布局文件相對應的 Activity
。像這樣重寫 onCreate(...)
方法:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 替換 setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
final Person person = new Person();
person.setName("張三");
binding.setPerson(person);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
person.setName("李四");
}
}, 3000);
}
其中,ActivityMainBinding
是根據之前編寫的布局文件 activity_main.xml
自動編譯生成的類。它接管了布局文件中聲明的所有綁定,我們可以通過 binding.setPerson(...)
或 binding.getPerson()
方法設置、訪問綁定的變量。
此外,binding
還帶來了另一個重要福利,這一知識點本應放在之后介紹,但我忍不住現在就告訴你:我們現在終于可以與 findViewById(...)
和 ButterKnife
說再見了!binding
會自動通過 id
創建對控件的引用。在本例中我們可以通過 binding.nameEt
直接訪問 id
為 nameEt
的 EditText
!雖然它不能直接訪問到通過 include
添加進布局的控件,但我們可以給被 include
的布局添加 layout
元素從而間接訪問其中的控件。
回到正題,除了示例中的方法,我們還可以通過以下方法在不同組件中添加綁定:
// Activity
ActivityMainBinding binding = MainActivityBinding.inflate(getLayoutInflater());
// Fragment
FragmentPageBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.fragment_page, viewGroup, false);
// or
FragmentPageBinding binding = FragmentPageBinding.inflate(layoutInflater, R.layout.fragment_page, viewGroup, false);
// ListView or RecyclerView
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
// or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
至此,我們已經可以運行程序查看效果了。運行之后可以看到界面上成功顯示出「張三」,但3秒后并沒有顯示「李四」。這是因為單純的使用 JavaBean
綁定數據時,DataBinding
無法自動刷新界面。此時我們需要對 JavaBean
進行一些改造,首先繼承 BaseObservable
類,然后在 getter
方法之前添加 Bindable
注解,在 setter
方法之內添加 notifyPropertyChanged(...)
方法,就像這樣:
// Person.class
public class Person extends BaseObservable{
private String name;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
}
再次運行代碼,界面應該可以正常顯示「李四」了。但使用這種方法比較麻煩,代碼量偏高,而且在實際使用時編譯環境可能經常報錯,所以我推薦使用 DataBinding
提供的另一種方式:ObserableField
。
除了 BaseObservable
,Databinding
還向我們提供了 ObserableField
用于快速創建綁定。ObserableField
通過泛型的方式實現對對象的支持,對于其它數據類型則實現了:ObserableBoolean
,ObserableByte
,ObserableChar
,ObserableShort
,ObserableInt
,ObserableLong
,ObserableFloat
,ObserableDouble
和 ObserableParcelable
。我們可以這樣使用:
// Pserson.class
public class Person {
public final ObservableField<String> name = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
當訪問變量時只需:
person.name.set("張三");
int age = person.age.get();
看起來不管用什么方法創建綁定都很簡單,但你可能已經發現一個問題:從 EditText
修改名字時 TextView
無法同步刷新,而 pserson.name
的值也沒有變化!修復這個問題實現數據的雙向綁定非常簡單。讓我們回到布局文件,然后將給 EditText
綁定數據時使用的 @{}
語法改為 @={}
,然后,就沒然后了!數據的雙向綁定就此完成!
尾巴
我計劃在下一篇文章里繼續介紹 DataBinding
的高級用法,如命令綁定等。