Android Jetpack系列——DataBinding 最佳實踐

看了谷歌官方文章確實寫的太簡略了,甚至看完以後有不少地方還不知道怎麼回事兒或者怎麼用,那麼接下來我將經過幾篇文章全面介紹一下 DataBinding 以及 DataBinding 的使用。
GitHub傳送門 歡迎Star 下載
若有任何問題 關注 「朝陽楊少爺」 公衆號給我留言,我會及時回覆。

寫在前面

咱們經過兩篇文章,分別介紹了 DataBinding(Android Jetpack系列——細說DataBinding
) ,以及 DataBinding 的簡單使用(DataBinding 的簡單使用
) ,這篇文章,咱們來介紹一下 DataBinding 最佳實踐——Binding adaptersjava

我之因此說 Binding adapters 是 DataBinding 的最佳實踐,是由於用過了才知道是真的好用!android

下面咱們就經過這篇文章全面的介紹一下 Binding adapters。數組

請耐心看完這篇文章,就知道真的好用!bash

DataBinding裏的註解方法講解

在正式介紹 Binding adapters 以前,咱們先了解一下 DataBinding 裏的註解方法。app

@Bindable

用於數據更新自動刷新視圖。框架

@BindingAdapter

這個註解用於支持自定義屬性,或者是修改原有屬性。註解值能夠是已有的 xml 屬性,例如 android:src、android:text等,也能夠自定義屬性而後在 xml 中使用。ide

列如官方示列當中,就介紹了個 「setPadding」 的例子。佈局

@BindingAdapter("android:paddingLeft")
fun setPaddingLeft(view: View, padding: Int) {
    view.setPadding(padding,
                view.getPaddingTop(),
                view.getPaddingRight(),
                view.getPaddingBottom())
}
複製代碼

接受多個屬性的適配器。ui

@BindingAdapter(value = { "imageUrl", "error" }, requireAll = false)
fun loadImage(view: ImageView, url: String, error: Drawable) {
    Picasso.get().load(url).error(error).into(view)
}

複製代碼

從上面,咱們能夠注意到幾個關鍵的地方:url

  1. 修飾的方法,必須是 public static
  2. 方法參數第一個要求必須是 View
  3. 方法名能夠隨意
  4. 最後一個 booblean 類型是可選參數。能夠要求是否全部參數都須要填寫,默認是true。
  5. 若是這裏requireAll爲false, 你沒有填寫的屬性值將爲null. 因此須要作非空判斷。

這裏須要特殊說明的是:

當發生衝突時,定義的綁定適配器將覆蓋Android框架提供的默認適配器。

@BindingMethods

DataBinding默承認以在佈局中使用setter方法做爲自定義屬性,

可是若是不是setter格式的方法就要使用BindingMethod註解了. 經過建立一個自定義屬性來關聯一個類中已有的方法。

該註解屬於一個容器. 內部參數是一個@BindingMethod數組, 只能用於修飾類(任意類均可以, 類能夠爲空).

下面咱們看一看官方示例:

@BindingMethods(value = [
    BindingMethod(
        type = android.widget.ImageView::class,
        attribute = "android:tint",
        method = "setImageTintList")])
複製代碼

這裏須要注意的是,這個註解必須有三個屬性。

  1. type:字節碼
  2. attribute:屬性
  3. method:方法

會在指定的字節碼(type)中尋找方法(method), 而後經過你建立的佈局屬性(Attribute)來回調方法。

若是屬性名和@BindingAdapter衝突會報錯

該註解只是單純地關聯已有的方法, 並不能新增方法. 因此全都是註解的空類.

@BindingConversion

屬性值自動進行類型轉換

列如,咱們用的 android:background 屬性是 Drawable 的,可是須要指定一個顏色值,而這個值是整數的。

那麼我就須要用到了 @BindingConversion 註解。

<View
   android:background="@{isError ? @color/red : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
複製代碼

這裏咱們就能夠用帶有bindingConversion註釋的靜態方法進行轉換,以下所示:

@BindingConversion
fun convertColorToDrawable(color: Int) = ColorDrawable(color)
複製代碼

可是,綁定表達式中提供的值類型必須一致。不能在同一表達式中使用不一樣的類型,列如以下的錯誤示範:

<View
   android:background="@{isError ? @drawable/error : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
複製代碼

經過,以上咱們能夠注意到:

  1. 只能修飾 public static 方法。
  2. 任意位置任意方法名都不被限制。
  3. DataBinding自動匹配被該註解修飾的方法和匹配參數類型。
  4. 返回值類型必須喝屬性setter方法匹配,且參數只能有一個。
  5. 要求屬性值必須是@{}DataBinding表達式。

Binding adapters的使用實踐

經過上面的介紹,咱們瞭解到了這幾個註釋方法,接下來,咱們就要開始使用這些方法。
下面就開始實踐使用:

@Bindable

這個註解的理解仍是十分簡單的。

使用 @Bindable 來標記的 get 方法,在編譯時,會在BR類當中生成對應的字段,而後與 notifyPropertyChanged() 方法配合使用,當該字段中的數據被修改時,dataBinding 會自動刷新對應view的數據,而不用咱們在拿到新數據後從新把數據在setText()一遍,就憑這一點,dataBinding就能夠簡化大量的代碼。

以此來實現雙向綁定,關於雙向綁定的內容,我會經過下一篇文章來詳細講述,如今先簡單介紹一下使用。

實體類也能夠不用繼承BaseObservable

,而是實現Observable接口,可是須要自行處理一些接口方法邏輯,BaseObservable是實現Observable接口的類,內部已經作好了相關邏輯處理,因此選擇繼承BaseObservable相對簡單一些。

接下來咱們看一下如何在代碼裏實現:

class StudentInfo : BaseObservable() {
    @get:Bindable
    var name: String? = null
    @get:Bindable
    var age: Int = 0
    @get:Bindable
    var sex: String? = null
    @get:Bindable
    var score: Int = 0
}
複製代碼

這樣,咱們的實體類就完成了。具體的使用方法和效果,咱們在以後講解雙向綁定的時候會着重介紹。

@BindingAdapter

這裏咱們必須着重介紹一下 BindingAdapter 這個註解。這個多是咱們在以後的使用當中,最經常使用的一個註解。
這個註解厲害了!
除了從新定義已經有的方法,還能夠定義新的屬性!

列如,咱們有個View既沒有android:xxx=""或者app:xxx=""屬性,也沒有setXxx()方法,咱們經過@BindingAdapter一樣能夠實現自定義android:xxx=""或者app:xxx=""屬性,而後使用!

除了定義屬性職位,咱們還能夠定義一些不屬於這個View的屬性!
咱們能夠經過 @BindingAdapter 自定義一個或者一些屬性,讓咱們能夠在這個View當中,使用相應的屬性!

例如咱們定一個ImageView經過 @BindingAdapter 來定義一些屬性。

@BindingAdapter(value = {"android:imageUrl", "android:placeHolder", "android:error"}, requireAll = false)
    public static void loadImage(ImageView view, String url, Drawable error, Drawable placeHolder) {
        Glide.with(view.getContext()).load(url).into(view);
    }
複製代碼

定義好以後,咱們就能夠開始使用了!

<ImageView
                android:id="@+id/iv_binding_adapter"
                android:layout_width="wrap_content" android:layout_height="wrap_content"
                android:placeHolder="@{@drawable/ic_launcher"
                android:imageUrl="@{url}"
                android:error="@{@drawable/ic_launcher}"
        />
複製代碼

值得注意的是,這裏的 @drawable/ic_launcher,用@{}括住資源使其成爲有效的綁定表達式。

可是,我有一點疑惑的是,如今咱們圖片的資源都放在mipmap系列文件夾下了,可是這裏設置只能在drawable文件夾下面找到對應的資源?若是有什麼想法和辦法歡迎給我留言,咱們交流一下!

還有一點注意的是,咱們設置glide的時候,別忘了在AndroidManifest文件當中把權限設置上!

<uses-permission android:name="android.permission.INTERNET" />
複製代碼

接下來,咱們就能夠看到咱們要實現的效果了

是否是很厲害!這樣,咱們能夠節約多少代碼!

以上只是一個簡單的使用,還有更厲害的!
那就是配合RecyclerView設置adapter。RecyclerView能夠說是咱們最經常使用的一個控件,若是吧adapter和DataBinding結合以後,你會發現寫Adapter會變得十分的簡單!
更多的屬性,均可以在xml當中完成。

咱們能夠爲RecyclerView的Adapter當中,設置咱們經常使用的一些屬性,好比 setOnItemClickListener 、 setOnLoadMoreListener 、
setEnableLoadMore 、setOnRefreshListener
等等這些咱們經常使用的一些方法。
列如,咱們能夠定義一個BindAdapter

public class RecyclerViewBindingAdapter {

    @BindingAdapter(value = {"android:onItemClick", "android:onLoadMore",
            "android:loadMoreEnable"}, requireAll = false)
    public static void setupAdapter(RecyclerView recyclerView, final ItemClickListener itemClickListener,
                                    final LoadMoreListener loadMoreListener, final boolean loadMoreEnable) {
        RecyclerView.Adapter adapter = recyclerView.getAdapter();
        if (adapter == null || !(adapter instanceof BaseQuickAdapter)) {
            return;
        }
        BaseQuickAdapter quickAdapter = (BaseQuickAdapter) adapter;
        quickAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                itemClickListener.onItemClick(adapter, view, position);
            }
        });

        quickAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {
            @Override
            public void onLoadMoreRequested() {
                loadMoreListener.onLoadMore();
            }
        }, recyclerView);
        quickAdapter.setEnableLoadMore(loadMoreEnable);
        quickAdapter.setLoadMoreView(new RVLoadMoreView());
        quickAdapter.openLoadAnimation(BaseQuickAdapter.ALPHAIN);
    }

    public interface ItemClickListener {
        void onItemClick(BaseQuickAdapter adapter, View view, int position);
    }

    public interface LoadMoreListener {
        void onLoadMore();
    }
}
複製代碼

在佈局文件當中,使用咱們剛纔定義的屬性

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:loadMoreEnable="@{true}"
    android:onItemClick="@{presenter.onItemClick}"
    android:onLoadMore="@{presenter.onLoadMore}"
    app:adapter="@{adapter}"
    app:layoutManager="LinearLayoutManager"/>

複製代碼

經過上面的方式,咱們就實現了經過在RecyclerView中配置屬性達到爲adapter設置點擊監聽,上拉加載監聽,以及是否開啓監聽的目的。
這裏值得注意的是:

其中的app:adapter="@{adapter}"是由於RecyclerView有setAdapter方法,結合databinding的特性,故而能夠這樣寫。而app:layoutManager="LinearLayoutManager"屬性是RecyclerView本身提供的一個屬性,爲了方便咱們爲RecyclerView設置layoutManager,其內部採用反射構造一個目標layoutManager,而後經過RecyclerView的public void setLayoutManager(LayoutManager layout)再進行設置。

最後

相信,經過上面的內容。已經能體會到了DataBinding的便捷之處。接下來,咱們在講講雙向綁定。若有任何問題,歡迎給我留言,咱們一塊兒討論。

GitHub傳送門 歡迎Star 下載
若有任何問題 關注 「朝陽楊少爺」 公衆號給我留言,我會及時回覆。

掃一掃,即刻加入到專屬限免的星球當中,這裏有不少有意思的人,好玩兒的事兒等你來耍!

相關文章
相關標籤/搜索