DataBinding入門進階指南(二)
前言
上一篇裡,主要講了關於Databinding的以下幾點:
- 接入與使用
- 簡單的資料繫結
- 點選事件的繫結
這一篇將會繼續上一篇的步伐,對DataBinding的使用更深幾分.首先依舊是從資料繫結開始
之前所介紹的,雖然UI與資料進行了繫結,但是修改資料物件的時候並不會同時更新 UI .
現在有三種不同型別的 observable 類: objects , fields , 還有 collections .
當其中某個 observable 資料物件繫結到 UI 並且資料物件的屬性發生更改時, UI 將自動更新,下面開始介紹.
Observable資料
如果你的資料類只有幾個屬性,那麼沒必要去實現 Observable 介面來監聽資料的改變,可以使用下面這些欄位:
- ofollow,noindex"> ObservableBoolean
- ObservableByte
- ObservableChar
- ObservableShort
- ObservableInt
- ObservableLong
- ObservableFloat
- ObservableDouble
- Parcelable.html" target="_blank" rel="nofollow,noindex"> ObservableParcelable
現在,我們再建立一個類
class ObservableBean { val text = ObservableField<String>() }
佈局檔案改為:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="model" type="com.test.project.testdatabinding.MVP.DataBinding.Bean.ObservableBean"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/et_test" android:text="@={model.text}" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_test" android:text="@{model.text}" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> </layout>
需要注意的是,上面的 EditText 的 text 屬性使用的是 @={} 而 TextView 使用的是 @{} 。當你想要使用雙向繫結的時候,可別忘了這個 = 號
Activity的程式碼只改變了繫結物件:
class DataBindingActivity<T> : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //setContentView(R.layout.activity_data_binding) val bindingBinding : ActivityDataBindingBinding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding) bindingBinding.model = ObservableBean() } }
效果如下:

image
可以看到,直接使用 Observable 欄位去實現雙向資料繫結是很輕鬆的,不過實際專案裡面需求各不相同,要將資料都換成 Observable 欄位還是很麻煩的,所以自定義雙向資料繫結非常有必要!
這時對 ObservableBean 進行修改:
class ObservableBean : BaseObservable() { @get:Bindable var text: String = "" set(value) { field = value notifyPropertyChanged(BR.text) } } //相較於Kotlin寫法,這裡我覺得Java寫法更有助於理解: publicclass ObservableBean extends BaseObservable { private String text; @Bindable public String getText() { return text; } public ObservableBean setText(String text) { this.text = text; notifyPropertyChanged(BR.text); return this; } }
修改過後的效果與之前使用 Observable 欄位的效果一樣,上面需要注意的兩點:
- 使用了 @Bindable 註解,進行繫結宣告
- 使用了 notifyPropertyChanged() 方法為資料重新整理做準備
不過在我看來,通過這種繼承的方法對於我們原有的資料結構並不過友好,尤其是繼承了 BaseObservable 類的資料類不能通過 Gson 去與 Json 相互轉換。
所以使用的時候,我們可以考慮通過某個中介類的方式去進行轉換.
佈局的繫結
第第一篇中,有寫到Activity中如何獲取自動生成的佈局繫結類——xxxBinding,這種類的命名與使用資料繫結的佈局檔案xml有關,比如說 activity_main.xml 就會生成 ActivityMainBinding
這裡再詳細說明一下,不同型別的佈局,應該怎麼獲取生成的 Binding繫結類
事先並不知道繫結型別的物件可以使用 DataBindingUtil 去建立繫結
val rootView = LayoutInflater.from(this).inflate(layoutId, parent, attachToParent) val binding: ViewDataBinding? = DataBindingUtil.bind(viewRoot)
獲取帶 ID 的 View物件
如果使用的是Kotlin,可以直接在Activity裡很方便的使用id獲取View物件,不過使用DataBinding後,就有另外一種獲取View物件的方式了
佈局裡面的 Id 如下:
... <EditText android:id="@+id/et_test" android:text="@={model.text}" android:layout_width="match_parent" android:layout_height="wrap_content" /> ...
通過 Id 獲取 EditText 的方法如下:
... super.onCreate(savedInstanceState) val bindingBinding : ActivityDataBindingBinding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding) bindingBinding.model = ObservableBean() val editText = bindingBinding.root.rootView.findViewById<EditText>(R.id.et_test)
高階繫結
有時候,特定的繫結類是未知的.
例如,針對任意佈局操作的 RecyclerView.Adapter 不知道特定的繫結類.它仍然必須在呼叫 onBindViewHolder() 方法時分配繫結值.
在 RecyclerView 的 onBindViewHolder() 方法裡,可以這樣寫:
override fun onBindViewHolder(holder: BindingHolder, position: Int) { item: T = mItems.get(position) holder.binding.setVariable(BR.item, item); holder.binding.executePendingBindings(); }
使用@BindingAdapter自定義繫結邏輯
DataBinding為我們提供了一種可以對繫結邏輯進行自定義的方法,比如說我想在xml中對一個ImageView控制元件載入圖片,並且是使用的Glide載入框架,這時候可以這樣:
//隨便建立一個類,然後在類中定義如下方法 @BindingAdapter("imageUrl") fun loadImage(view: ImageView, url: String) { GlideApp.with(view.getContext()) .load(url) .fitCenter() .into(view); }
使用的時候編譯器會自動生成對應屬性:
<ImageView app:imageUrl="@{model.imageUrl}"/>
使用 @BindAdapter 幾乎可以完成你想要的各種邏輯,不過我覺得,只有那種使用率特別高的程式碼,才最適合這個屬性.
使用@BindingConversion完成轉換功能
在某些情況下,特定型別之間需要自定義轉換。 例如,檢視的android:background屬性需要Drawable,但指定的顏色值是整數。
官方例子中,轉換功能的具體用法如下:
@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"/>
暫歇
本篇關於DataBindin的介紹也就到此結束,不過DataBinding的使用還沒有到頭,下一篇將會側重實際上的操作以及DataBinding還可以為我們帶來哪些便捷.