怎么配置DataBinding
在Module的gradle 文件下车份,AndroidStudio版本不通開啟的方式也不同
AndroidStudio 3.x 版本
android {
...
dataBinding{
enabled = true
}
}
AndroidStudio 4.x 版本(也可以使用3.0的方式開啟哎媚,但是4.0推薦使用下面的方法)
android {
...
buildFeatures{
dataBinding = true
}
}
這里有一個坑萤衰,就是如果子Module配置了DataBinding,必須在主Module也配置
怎么讓一個 View 可以綁定數(shù)據(jù)
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.appcompat.widget.LinearLayoutCompat>
</layout>
比原來的layout文件多了layout
標(biāo)簽和data
標(biāo)簽抚吠,這樣格式的xml布局文件就是一個支持DataBinding的布局文件冀痕,可以通過快捷鍵Alt
+Enter
鍵來快速生成
DataBinding 中的被觀察者 BaseObservable
如果想要你的UI隨著數(shù)據(jù)的更新而更新,還需要實現(xiàn)BaseObservable
BaseObservable 是一個被觀察者萝毛,UI組件會觀察BaseObservable项阴,如果數(shù)據(jù)發(fā)生了變化,則UI會更新笆包。這里可以把BaseObservable 理解為數(shù)據(jù)的包裝類鲁冯。
看一個簡單的實現(xiàn)
public class UserEntity extends BaseObservable {
@Bindable
private String nickname;
@Bindable
private String headUrl;
public UserEntity(String nickname, String headUrl) {
this.nickname = nickname;
this.headUrl = headUrl;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
notifyPropertyChanged(BR.nickname);
}
public String getHeadUrl() {
return headUrl;
}
public void setHeadUrl(String headUrl) {
this.headUrl = headUrl;
notifyPropertyChanged(BR.headUrl);
}
}
首先數(shù)據(jù)類實現(xiàn)BaseObservable,然后對需要被觀察的數(shù)據(jù)用@Bindable
注解
重寫set/get 方法色查,然后在set 方法(數(shù)據(jù)更新的地方)調(diào)用notifyPropertyChanged(BR.id)
薯演,UI就會被通知刷新UI
BR.id是DataBinding生成的id,用來區(qū)分數(shù)據(jù)的id秧了,如果BR下面沒有你的id跨扮,先檢查一個有沒有注解,然后再Rebuild 一下
單向綁定
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="userInfo"
type="com.xiaoyu.mvvmdemo.UserEntity" />
</data>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@{userInfo.nickname}" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/nickname_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text='@{"昵稱:"+userInfo.nickname}' />
</androidx.appcompat.widget.LinearLayoutCompat>
</layout>
單向綁定的話比較簡單,通過@{}
的方式就可以把數(shù)據(jù)綁定到UI上了
這里寫了兩個綁定衡创,一個是""
帝嗡,一個是''
,如果有做字符串操作的(比如例子中的拼接字符串)璃氢,是不能使用""
進行綁定的
雙向綁定
雙向綁定相對于單向綁定的語法上來說哟玷,只多了一個=
號
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="請輸入賬號"
android:text="@={accountNumber}" />
雙向綁定在使用的過程中要注意到一點,就是綁定的這個屬性一也,是否支持雙向綁定巢寡,如果不支持,編譯肯定是不會通過的椰苟。
下面是谷歌實現(xiàn)的雙向綁定
- AbsListView
- android:selectedItemPosition
- CalendarView
- android:date
- CompoundButton
- android:checked
- DatePicker
- android:year
- android:month
- android:day
- NumberPicker
- android:value
- RadioGroup
1.android:checkedButton - RatingBar
- android:rating
- SeekBar
- android:progress
- TabHost
- android:currentTab
- TextView
- android:text
- TimePicker
- android:hour
- android:minute
那么如果說系統(tǒng)提供的綁定方法不能夠?qū)崿F(xiàn)現(xiàn)有的需求怎么辦抑月?這里就需要到下面的自定義綁定方法了
自定義綁定 - 單向綁定
自定義單向綁定比較簡單。只需要一個注解舆蝴,一個方法就可以了
這里寫一個用Glide加載網(wǎng)圖谦絮,并且設(shè)置占位圖的綁定方法
@BindingAdapter(value = {"bindUrl", "bindPlaceholder"}, requireAll = false)
public static void bindUrlAndPlaceholder(ImageView view, String url, int placeholder) {
Glide.with(view).load(url).apply(new RequestOptions().placeholder(placeholder)).into(view);
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.xiaoyu.mvvmdemo.R" />
<variable
name="userInfo"
type="com.xiaoyu.mvvmdemo.UserEntity" />
</data>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
bindPlaceholder="@{R.drawable.ic_launcher_background}"
bindUrl="@{userInfo.headUrl}"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="50dp" />
</androidx.appcompat.widget.LinearLayoutCompat>
</layout>
@BindingAdapter
這個注解是用來說明,我這個方法是一個綁定方法注解里需要兩個參數(shù)value
和requireAll
value
的類型是一個數(shù)組洁仗,數(shù)組的長度為方法參數(shù)長度-1(去掉View參數(shù))层皱,然后順序一一對應(yīng),bindUrl
和url
對應(yīng)赠潦,bindPlaceholder
和placeholder
對應(yīng)叫胖,view
即被綁定的View
requireAll
這個參數(shù)默認為true
,它的意思是祭椰,value
里面的數(shù)據(jù)是否全部都要綁定
自定義綁定 - 雙向綁定
單向綁定可以只是set方法臭家,雙向綁定需要做的是,當(dāng)被觀察的View屬性發(fā)生變化的時候方淤,需要把自身的值給更新钉赁。用SwipeRefreshLayout
舉一個栗子
@BindingAdapter("bindIsRefreshing")
public static void setRefreshing(SwipeRefreshLayout refreshLayout, boolean isRefreshing) {
if (refreshLayout.isRefreshing() != isRefreshing) {
refreshLayout.setRefreshing(isRefreshing);
}
}
@InverseBindingAdapter(attribute = "bindIsRefreshing", event = "onRefreshChange")
public static boolean isRefreshing(SwipeRefreshLayout refreshLayout) {
return refreshLayout.isRefreshing();
}
@BindingAdapter("onRefreshChange")
public static void setOnRefreshListener(SwipeRefreshLayout refreshLayout, InverseBindingListener inverseBindingListener) {
refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
inverseBindingListener.onChange();
}
});
}
setRefreshing
這個方法和 單向綁定一樣,不需要多解釋携茂。
isRefreshing
和setOnRefreshListener
這兩個方法需要對照著看你踩,
先看isRefreshing
這個方法,返回了SwipeRefreshLayout.isRefreshing()
讳苦,然后看上面的@InverseBindingAdapter
注解带膜,有兩個參數(shù),attribute
表示被綁定的屬性鸳谜,我們上面因為是定義的bindIsRefreshing膝藕,所以這里和上面一樣bindIsRefreshing
event
這個參數(shù),可以理解為isRefreshing
的調(diào)用時機咐扭。這里轉(zhuǎn)到底三個方法setOnRefreshListener
,這個方法中的InverseBindingListener
是一個DataBinding的接口芭挽,需要在屬性發(fā)生變化時回調(diào)onChange
方法滑废,這樣雙向綁定的觀察者就會知道屬性發(fā)生了變化,就會更新自身的屬性值
當(dāng)栗子上的SwipeRefreshLayout
觸發(fā)OnRefreshListener.onRefresh
時袜爪,會回調(diào)到InverseBindingListener.onChange
方法蠕趁,然后觀察者收到回調(diào)后,就會調(diào)用isRefreshing
方法來更新屬性
需要注意的是辛馆,雙向綁定中的set方法一定要加屬性值得判斷俺陋,就是當(dāng)屬性相同時,不調(diào)用View的set方法昙篙,避免觸發(fā)死循環(huán)