通過前面的學(xué)習(xí),我們已經(jīng)會使用DataBinding的一些基礎(chǔ)語法了,而這一篇,我就和大家一起開啟DataBinding的進(jìn)階之旅,下面就讓我們通過幾個(gè)小例子一起起飛吧。
1.BindingAdapter注解設(shè)置自定義屬性
BindingAdapter對于自定義屬性是非常有用的,舉個(gè)例子,如果你的項(xiàng)目中用到了Glide,或者是其他的圖片加載框架,由于這些框架一般通過url給ImageView設(shè)置圖片的,但是ImageView中并沒有設(shè)置url的屬性,這個(gè)時(shí)候,BindingAdapter就派上用場了。一起來看看是如何實(shí)現(xiàn)的:
public class ImageHelper {
/**
* 1.加載圖片,無需手動(dòng)調(diào)用此方法
* 2.使用@BindingAdapter注解設(shè)置自定義屬性的名稱,imageUrl就是屬性的名稱,
* 當(dāng)ImageView中使用imageUrl屬性時(shí),會自動(dòng)調(diào)用loadImage方法,
*
* @param imageView ImageView
* @param url 圖片地址
*/
@BindingAdapter({"imageUrl"})
public static void loadImage(ImageView imageView, String url) {
Glide.with(imageView.getContext()).load(url)
.placeholder(R.mipmap.fruit)
.error(R.mipmap.fruit)
.into(imageView);
}
}
使用@BindingAdapter注解設(shè)置自定義屬性的名稱,如上所示,imageUrl就是屬性的名稱,當(dāng)ImageView中使用imageUrl屬性時(shí),會自動(dòng)調(diào)用loadImage方法,參數(shù)imageView為當(dāng)前使用imageUrl屬性的ImageView,參數(shù)url為圖片地址。
xml中使用自定義屬性
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="com.zx.databindingdemo.bean.UserBean" />
<variable
name="user"
type="UserBean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:orientation="vertical">
<!-- 當(dāng)imageUrl屬性存在時(shí),會自動(dòng)調(diào)用ImageHelper的loadImage方法 -->
<ImageView
android:layout_width="120dp"
android:layout_height="120dp"
android:scaleType="centerCrop"
app:imageUrl="@{user.picUrl}" />
</LinearLayout>
</layout>
Activity中設(shè)置圖片的url
public class BasicActivity extends AppCompatActivity {
//用戶頭像
private static final String URL_USER_PIC = "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=4138850978,2612460506&fm=200&gp=0.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityBasicBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_basic);
UserBean userBean = new UserBean(URL_USER_PIC, "張三", 24);
binding.setUser(userBean);
}
}
這樣就可以在DataBinding中使用Glide來加載圖片了,此外,BindingAdapter也是允許設(shè)置多個(gè)屬性的,如下:
@BindingAdapter({"imageUrl", "placeHolder",error"})
public static void loadImage(ImageView view, String url, Drawable holderDrawable, Drawable errorDrawable) {
Glide.with(imageView.getContext())
.load(url)
.placeholder(holderDrawable)
.error(errorDrawable)
.into(imageView);
}
2.數(shù)據(jù)對象 (Data Objects)
任何plain old Java object (POJO)對象都能用在 Data Binding 中,但是有個(gè)問題,當(dāng)我們的數(shù)據(jù)源發(fā)生改變的時(shí)候,UI界面上顯示的還是之前的數(shù)據(jù),并沒有同步更新,這顯然是不行的。不知道大家還記不記得,RecyclerView或者ListView是如何更新數(shù)據(jù)的?數(shù)據(jù)是通過Adapter提供的,當(dāng)數(shù)據(jù)發(fā)生改變時(shí),我們通過notifyDatasetChanged通過UI去改變數(shù)據(jù),這是通過一個(gè)觀察者模式來實(shí)現(xiàn)的,很幸運(yùn),查閱源碼后發(fā)現(xiàn)DataBinding也是通過觀察者模式來實(shí)現(xiàn)的。
DataBinding動(dòng)態(tài)更新數(shù)據(jù)的2種方式
- BaseObservable
- ObservableFields
BaseObservable
我們可以通過Observable的方式去通知UI刷新數(shù)據(jù),Observable 是一個(gè)包含添加/移除 listener 的機(jī)制的接口,為了簡化開發(fā),Android 原生提供了一個(gè)基類 BaseObservable 來實(shí)現(xiàn) listener 注冊機(jī)制。這個(gè)類也實(shí)現(xiàn)了字段變動(dòng)的通知,只需要在 getter 上使用 Bindable 注解,并在 setter 中通知更新即可。
定義一個(gè)實(shí)體類,并繼承BaseObservable
public class DoubleBindBean extends BaseObservable {
//BR 是編譯階段生成的一個(gè)類,功能與 R.java 類似,用 @Bindable 標(biāo)記過 getter 方法會在 BR 中生成一個(gè) entry,當(dāng)我們
//通過代碼可以看出,當(dāng)數(shù)據(jù)發(fā)生變化時(shí)還是需要手動(dòng)發(fā)出通知。 通過調(diào)用notifyPropertyChanged(BR.firstName)來通知系統(tǒng) BR.firstName 這個(gè) entry 的數(shù)據(jù)已經(jīng)發(fā)生變化,需要更新 UI。
private String content; //內(nèi)容
public DoubleBindBean(String content) {
this.content = content;
}
@Bindable
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
notifyPropertyChanged(BR.content); //通知系統(tǒng)數(shù)據(jù)源發(fā)生變化,刷新UI界面
}
}
BR 是編譯階段生成的一個(gè)類,功能與 R.java 類似,用 @Bindable 標(biāo)記過 getxxx() 方法會在 BR 中生成一個(gè) entry。 當(dāng)數(shù)據(jù)發(fā)生變化時(shí)需要調(diào)用 notifyPropertyChanged(BR.content) 通知系統(tǒng) BR.content這個(gè) entry 的數(shù)據(jù)已經(jīng)發(fā)生變化以更新UI。
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>
<variable
name="doubleBindBean"
type="com.zx.databindingdemo.bean.DoubleBindBean" />
<variable
name="onClickListener"
type="android.view.View.OnClickListener" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{doubleBindBean.content}" />
<Button
android:id="@+id/change_content_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{onClickListener}"
android:text="BaseObservable方式改變內(nèi)容" />
</LinearLayout>
</layout>
Activity:
public class DoubleBindActivity extends AppCompatActivity implements View.OnClickListener {
private DoubleBindBean doubleBindBean;
private boolean flag;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDoubleBindBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_double_bind);
doubleBindBean = new DoubleBindBean("我是原始內(nèi)容");
binding.setDoubleBindBean(doubleBindBean);
binding.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.change_content_btn:
//BaseObservable
flag = !flag;
if (flag) {
doubleBindBean.setContent("我是更新后的內(nèi)容");
} else {
doubleBindBean.setContent("我是原始內(nèi)容");
}
break;
}
}
ObservableFields
如果想要省時(shí),或者數(shù)據(jù)類的字段很少的話,可以使用 ObservableField 以及它的派生 ObservableBoolean、 ObservableByte ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble、 ObservableParcelable 等。
它的實(shí)現(xiàn)就更簡潔了,如下
public class DoubleBindBean2 {
//變量需要為public
public final ObservableField<String> username = new ObservableField<>();
}
Activity:
public class DoubleBindActivity extends AppCompatActivity implements View.OnClickListener {
private DoubleBindBean2 doubleBindBean2;
private boolean flag2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDoubleBindBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_double_bind);
doubleBindBean2 = new DoubleBindBean2();
doubleBindBean2.username.set("我是原始內(nèi)容2");
binding.setDoubleBindBean2(doubleBindBean2);
binding.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.change_content_btn2:
//ObservableFields
flag2 = !flag2;
if (flag2) {
doubleBindBean2.username.set("我是更新后的內(nèi)容2");
} else {
doubleBindBean2.username.set("我是原始內(nèi)容2");
}
break;
}
}
}
Observable Collections
除了支持ObservableField,ObservableBoolean,ObservableInt等基礎(chǔ)變量類型以外,當(dāng)然也支持集合框架拉,比如:ObservableArrayMap,ObservableArrayList。使用和普通的Map、List基本相同
<?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>
<variable
name="list"
type="android.databinding.ObservableArrayList<String>" />
<variable
name="map"
type="android.databinding.ObservableArrayMap<String,Object>" />
<variable
name="onClickListener"
type="android.view.View.OnClickListener" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{list[0]}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{map["key0"]}' />
<Button
android:id="@+id/change_content_btn3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{onClickListener}"
android:text="list改變內(nèi)容" />
<Button
android:id="@+id/change_content_btn4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{onClickListener}"
android:text="map改變內(nèi)容" />
</LinearLayout>
</layout>
Activity:
public class DoubleBindActivity extends AppCompatActivity implements View.OnClickListener {
private ObservableArrayList<String> list=new ObservableArrayList<>();
private ObservableArrayMap<String,Object> map = new ObservableArrayMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDoubleBindBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_double_bind);
list.add("list1");
list.add("list2");
binding.setList(list);
map.put("key0","key0_value0");
map.put("key1","key1_value1");
binding.setMap(map);
binding.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.change_content_btn3:
list.set(0,"after change list");
break;
case R.id.change_content_btn4:
map.put("key0","after change key0_value0");
break;
}
}
}
View with ID
如果我們需要在Activity中獲取某個(gè)View來進(jìn)行一些操作,那該怎么辦呢?別擔(dān)心,有辦法,布局中每一個(gè)帶有 ID 的 View,都會生成一個(gè) public final 字段。binding 過程會做一個(gè)簡單的賦值,在 binding 類中保存對應(yīng) ID 的 View。這種機(jī)制相比調(diào)用 findViewById 效率更高。舉個(gè)例子:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.connorlin.databinding.model.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/userName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.userName}"/>
</LinearLayout>
</layout>
Activity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this,
R.layout.activity_main);
binding.userName.setText("content")
}
}
歡迎學(xué)習(xí)交流這個(gè)系列的文章
DataBinding系列(一):DataBinding初認(rèn)識
DataBinding系列(二):DataBinding的基本用法
DataBinding系列(三):RecyclerView中使用DataBinding