BindingAdapter
1.什么是 BindingAdapter
BindingAdapter :綁定適配器,是 Jetpack DataBinding 中用來擴展布局 xml 屬性行為的注解,允許你針對布局 xml 中的一個或多個屬性進行綁定行為擴展,這個屬性可以是自定義屬性,也可以是原生屬性。這個擴展行為可以是簡單的ViewModel屬性與控件賦值綁定,也可以是關聯某個控件屬性的額外操作,例如在設置屬性之前進行值域檢查,或類型轉換,或者統一處理一些事情。
例:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@{"http://****/**.jpg"}" />
上例中:src是默認的控件屬性,而src需要使用一個資源ID,來完成賦值,並不支持網絡地址來獲取設置圖片,此時就可以通過 BindingAdapter 來擴展行為,使其具備使用一個網絡地址,並且從網絡下載圖片並顯示的能力。
@BindingAdapter("android:src")
public static void setSrc(ImageView view, String bitmapPath) {
if(ObjectUtil.nonNull(bitmapPath)) {
GlideApp.with(view.getContext())
.load(bitmapPath)
.into(view);
} else {
view.setImageDrawable(null);
}
}
這樣就完成了對於 android:src 布局屬性的行為擴展。此時我們可以直接設置地址路徑來完成網絡下載圖片並復制給 ImageView 的能力了。
2.工作機制
BindingAdapter 是基於 APT 注解技術工作的,APT 可以在項目構建時更具相關編寫規則生成特定代碼完成一些指定功能的技術。
BindingAdapter 有兩個屬性,value 是一個 String[] ,requireAll 是一個boolean類型:
value 用來描述 XML 中感興趣的關聯屬性,這里是個數組,說明一個擴展方法可以同時關注多個 XML 屬性。這個很重要,有時候我們可能在對控件進行賦值時,需要同時給定多個值,而 XML 屬性值卻一次只能指定一個變量,這樣可能會讓我們不的不為此去擴展一個復合對象來完成這樣的賦值,但是實際上並不需要那樣。
requireAll 用來對 value 補充說明的,這個值默認是 true,表示使用這個適配規則必須在 XML 中聲明該注解關注所有屬性值,否則編譯時會報錯,而 false 則不需要,他允許只使用關注的部分或全部屬性來使用該規則。
例: 當requireAll = true 時
@BindingAdapter(value = {"galleryEffectWidthLeft", "galleryEffectWidthRight", "galleryEffectPageMargin", "galleryEffectScale"}, requireAll = true)
public static void setBannerGalleryEffect(Banner banner, int galleryWidthLeft, int galleryWidthRight, int galleryPageMargin, float galleryScale) {
banner.setBannerGalleryEffect(galleryWidthLeft, galleryWidthRight, galleryPageMargin, galleryScale);
}
//正確 galleryEffect 相關4個熟悉都設置了
<com.youth.banner.Banner
android:layout_width="match_parent"
android:layout_height="175dp"
android:layout_marginTop="90dp"
app:adapter="@{recommendVipZoneViewModel.bannerAdapter}"
app:banner_radius="8dp"
app:galleryEffectWidthLeft="@{7}"
app:galleryEffectWidthRight="@{8}"
app:galleryEffectPageMargin="@{9}"
app:galleryEffectScale="@{0.85f}" />
//錯誤,app:galleryEffectScale 屬性未設置情況
<com.youth.banner.Banner
android:layout_width="match_parent"
android:layout_height="175dp"
android:layout_marginTop="90dp"
app:adapter="@{recommendVipZoneViewModel.bannerAdapter}"
app:banner_radius="8dp"
app:galleryEffectWidthLeft="@{7}"
app:galleryEffectWidthRight="@{8}"
app:galleryEffectPageMargin="@{9}" />
例: 當requireAll = false 時
@BindingAdapter(value = {"galleryEffectWidthLeft", "galleryEffectWidthRight", "galleryEffectPageMargin", "galleryEffectScale"}, requireAll = false)
public static void setBannerGalleryEffect(Banner banner, int galleryWidthLeft, int galleryWidthRight, int galleryPageMargin, float galleryScale) {
banner.setBannerGalleryEffect(galleryWidthLeft, galleryWidthRight, galleryPageMargin, galleryScale);
}
//正確,需要注意的是,此時少了的galleryEffectScale屬性可能會接受一個默認只比如這里float可能為0.0
<com.youth.banner.Banner
android:layout_width="match_parent"
android:layout_height="175dp"
android:layout_marginTop="90dp"
app:adapter="@{recommendVipZoneViewModel.bannerAdapter}"
app:banner_radius="8dp"
app:galleryEffectWidthLeft="@{7}"
app:galleryEffectWidthRight="@{8}"
app:galleryEffectPageMargin="@{9}" />
3.如何使用
關於 BindingAdapter 注解本身已經有所了解。如何使用
首先,關於使用 BindingAdapter 描述的函數簽名,參數列表必須按照以下固定形式出現:
// 類構建可以隨意,APT會在構建時掃描全局代碼
public class ViewAttrAdapter {
// 需要注意的是 XML標簽 關注了幾個,參數列表就需要寫幾個對應的接受參數。且關注控件類必須在第一個參數。
@BindingAdapter({xml屬性標簽, ...})
public static void 函數名(關注的控件類 view, xml屬性標簽值 value, ...){
// 行為
}
// 可以包含多個函數
.....
}
而在XML布局中,我們應該使用 BindingAdapter 標簽對應的標簽類型進行賦值,切必須使用@{}作為值包裹,這是為了提供給APT解析XML是區別普通賦值的方式。
<View
android:layout_width="match_parent"
android:layout_height="175dp"
app:xml屬性標簽="@{函數參數接受類型的值}" />
需要注意的是綁定解析的觸發規則:
// 這個限定使用時一定是android:src才能匹配
@BindingAdapter("android:src")
// 這個限定使用時 app:src 和 android:src 都可以匹配,但需要注意的是,android必須是在自定義屬性聲明的XML中有描述過的,如 attrs.xml 中
@BindingAdapter("src")
觸發解析的屬性必須滿足 標簽="@{value}" 這里的 @{} 一定不能丟。
最后總結:BindingAdapter 提供了一個高自由的切片編程能力,允許在xml解析是綁定擴展行為,可以幫助完成很多事情,如事件監聽,屬性賦值,類型轉換,統一業務處理,比如埋點處理,防重按等等。而 BindingAdapter 僅僅是Databinding中一個使用較多的注解,之后會學習更多相關注解幫助擴展雙向綁定,雙向賦值之類的事情。(一上屬於個人理解,如有不同想法,可以溝通修改,也會不時更新)