DataBinding系列(四):DataBinding進(jìn)階之路

通過前面的學(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&lt;String&gt;" />

        <variable
            name="map"
            type="android.databinding.ObservableArrayMap&lt;String,Object&gt;" />

        <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")
    }  
}

github源碼下載

歡迎學(xué)習(xí)交流這個(gè)系列的文章
DataBinding系列(一):DataBinding初認(rèn)識
DataBinding系列(二):DataBinding的基本用法
DataBinding系列(三):RecyclerView中使用DataBinding

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容