DataBinding系列(四):DataBinding進階之路

wyfem發表於2021-09-09

透過前面的學習,我們已經會使用DataBinding的一些基礎語法了,而這一篇,我就和大家一起開啟DataBinding的進階之旅,下面就讓我們透過幾個小例子一起起飛吧。

1.BindingAdapter註解設定自定義屬性

BindingAdapter對於自定義屬性是非常有用的,舉個例子,如果你的專案中用到了Glide,或者是其他的圖片載入框架,由於這些框架一般透過url給ImageView設定圖片的,但是ImageView中並沒有設定url的屬性,這個時候,BindingAdapter就派上用場了。一起來看看是如何實現的:

public class ImageHelper {    /**
     * 1.載入圖片,無需手動呼叫此方法
     * 2.使用@BindingAdapter註解設定自定義屬性的名稱,imageUrl就是屬性的名稱,
     * 當ImageView中使用imageUrl屬性時,會自動呼叫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註解設定自定義屬性的名稱,如上所示,imageUrl就是屬性的名稱,當ImageView中使用imageUrl屬性時,會自動呼叫loadImage方法,引數imageView為當前使用imageUrl屬性的ImageView,引數url為圖片地址。

xml中使用自定義屬性



    
        
        
    

    

        <!-- 當imageUrl屬性存在時,會自動呼叫ImageHelper的loadImage方法 --&gt
        
    

Activity中設定圖片的url

public class BasicActivity extends AppCompatActivity  {    //使用者頭像
    private static final String URL_USER_PIC = "";    @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也是允許設定多個屬性的,如下:

@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.資料物件 (Data Objects)

任何plain old Java object (POJO)物件都能用在 Data Binding 中,但是有個問題,當我們的資料來源發生改變的時候,UI介面上顯示的還是之前的資料,並沒有同步更新,這顯然是不行的。不知道大家還記不記得,RecyclerView或者ListView是如何更新資料的?資料是透過Adapter提供的,當資料發生改變時,我們透過notifyDatasetChanged透過UI去改變資料,這是透過一個觀察者模式來實現的,很幸運,查閱原始碼後發現DataBinding也是透過觀察者模式來實現的。

DataBinding動態更新資料的2種方式

  • BaseObservable

  • ObservableFields

BaseObservable
我們可以透過Observable的方式去通知UI重新整理資料,Observable 是一個包含新增/移除 listener 的機制的介面,為了簡化開發,Android 原生提供了一個基類 BaseObservable 來實現 listener 序號產生器制。這個類也實現了欄位變動的通知,只需要在 getter 上使用 Bindable 註解,並在 setter 中通知更新即可。

定義一個實體類,並繼承BaseObservable

public class DoubleBindBean extends BaseObservable {    //BR 是編譯階段生成的一個類,功能與 R.java 類似,用 @Bindable 標記過 getter 方法會在 BR 中生成一個 entry,當我們
    //透過程式碼可以看出,當資料發生變化時還是需要手動發出通知。 透過呼叫notifyPropertyChanged(BR.firstName)來通知系統 BR.firstName 這個 entry 的資料已經發生變化,需要更新 UI。

    private String content; //內容

    public DoubleBindBean(String content) {        this.content = content;
    }    @Bindable
    public String getContent() {        return content;
    }    public void setContent(String content) {        this.content = content;
        notifyPropertyChanged(BR.content); //通知系統資料來源發生變化,重新整理UI介面
    }
}

BR 是編譯階段生成的一個類,功能與 R.java 類似,用 @Bindable 標記過 getxxx() 方法會在 BR 中生成一個 entry。 當資料發生變化時需要呼叫 notifyPropertyChanged(BR.content) 通知系統 BR.content這個 entry 的資料已經發生變化以更新UI。

xml:



    
        

        

    

    

        

        

    

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("我是原始內容");
        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("我是更新後的內容");
                } else {
                    doubleBindBean.setContent("我是原始內容");
                }                break;
    }
}

ObservableFields
如果想要省時,或者資料類的欄位很少的話,可以使用 ObservableField 以及它的派生 ObservableBoolean、 ObservableByte ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble、 ObservableParcelable 等。

它的實現就更簡潔了,如下

public class DoubleBindBean2 {    //變數需要為public
    public final ObservableField 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("我是原始內容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("我是更新後的內容2");
                } else {
                    doubleBindBean2.username.set("我是原始內容2");
                }                break;
        }
    }
}

Observable Collections

除了支援ObservableField,ObservableBoolean,ObservableInt等基礎變數型別以外,當然也支援集合框架拉,比如:ObservableArrayMap,ObservableArrayList。使用和普通的Map、List基本相同



    
        " />

        " />

        

    

    

        

        

        


        

    

Activity:

public class DoubleBindActivity extends AppCompatActivity implements View.OnClickListener {

    private ObservableArrayList list=new ObservableArrayList();    private ObservableArrayMap 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中獲取某個View來進行一些操作,那該怎麼辦呢?別擔心,有辦法,佈局中每一個帶有 ID 的 View,都會生成一個 public final 欄位。binding 過程會做一個簡單的賦值,在 binding 類中儲存對應 ID 的 View。這種機制相比呼叫 findViewById 效率更高。舉個例子:


   
       
   
   
       
   

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

原文連結:http://www.apkbus.com/blog-927424-75868.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2894/viewspace-2813525/,如需轉載,請註明出處,否則將追究法律責任。

相關文章