DataBinding-使用篇

概念

DataBind 就是 基于apt技術(shù),幫我們生成了一些模板代碼,這些模板代碼大概解決了如下操作:

  1. 控件變量的聲明,類似如下:
 @NonNull
 public final TextView tv1;//自動(dòng)解決了類型匹配的問題,不用擔(dān)心自己手抖觸發(fā)類型轉(zhuǎn)換異常了
  1. 控件的查找賦值,相當(dāng)于自動(dòng)幫我們完成了類似如下操作:
  tv1 = findViewById(R.id.tv1) //確保了自己腦子卡,忘記給聲明的變量賦值,引發(fā)空指針
  1. 控制的數(shù)據(jù)填充操作,也就是其本意數(shù)據(jù)綁定,類似自動(dòng)完成了如下操作:
tv1.setText(user.getName())

讀前須知

  1. 官網(wǎng)連接:數(shù)據(jù)綁定庫
  2. 本文只講使用層面的對(duì)應(yīng)解析,不涉及原理流程之類,這點(diǎn)將在下一篇完善
  3. 本文主要基于官網(wǎng)的使用實(shí)例,集合kapt生成的相關(guān)代碼,來吃透用法背后的真實(shí)面紗(源碼)
  4. 本文不涉及配置,基礎(chǔ)引用等,需要有一定的DataBinding使用經(jīng)驗(yàn),但是只用但是不知道為啥這么用的大佬

按照官網(wǎng)的順序一點(diǎn)一點(diǎn)來

最常見的TexView的text填充
< android:text="@{viewModel.name}" />

對(duì)應(yīng)的賦值模板代碼如下:

//聲明數(shù)據(jù)對(duì)應(yīng)數(shù)據(jù)變量
java.lang.String viewModelName = null;
//數(shù)據(jù)變量賦值
viewModelName = viewModel.getName();
//數(shù)據(jù)綁定View
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv1, viewModelName);
//TextViewBindingAdapter是DataBinding庫給我們提供的一個(gè)數(shù)據(jù)綁定適配器
//這個(gè)方法翻譯過來就是接管了android:text的屬性的賦值邏輯,也就是當(dāng)遇到上面寫法時(shí),會(huì)通過下面的靜態(tài)方法進(jìn)行賦值
//官方提供的了很多,可以參考著進(jìn)行更多屬性賦值功能擴(kuò)展,這絕對(duì)是最好的copy對(duì)象
   @BindingAdapter("android:text")
    public static void setText(TextView view, CharSequence text) {
        //下面實(shí)現(xiàn)很貼心,對(duì)新老內(nèi)容進(jìn)行了比較,防止了重復(fù)調(diào)用view.setText(text);引起的過渡繪制,很值得我們學(xué)習(xí)哦
        final CharSequence oldText = view.getText();
        if (text == oldText || (text == null && oldText.length() == 0)) {
            return;
        }
        if (text instanceof Spanned) {
            if (text.equals(oldText)) {
                return; // No change in the spans, so don't set anything.
            }
        } else if (!haveContentsChanged(text, oldText)) {
            return; // No content changes, so don't set anything.
        }
        view.setText(text);
    }
帶表達(dá)式的
< android:visibility="@{viewModel.age > 10 ? View.VISIBLE : View.GONE}" />

對(duì)應(yīng)的賦值代碼塊如下(上面只是一個(gè)簡(jiǎn)單的表達(dá)式,其實(shí)還有很多,但是官方并不太建議在布局寫太復(fù)雜的表達(dá)式,這樣會(huì)搞的布局文件很亂,按照J(rèn)etpack的整體思路,數(shù)據(jù)的邏輯處理,應(yīng)該的ViewModel中處理,布局里最好是取最終值就好,這里吐槽一點(diǎn),有些說法是Databind會(huì)搞的布局文件很亂,其實(shí)是用復(fù)雜了而已,人家官方本意其實(shí)指向讓你進(jìn)行數(shù)據(jù)綁定,并不想讓你在布局里做太復(fù)雜的數(shù)據(jù)邏輯)

//聲明變量,這個(gè)名字很長(zhǎng)
int viewModelAgeInt10ViewVISIBLEViewGONE = 0;
//變量賦值
viewModelAgeInt10ViewVISIBLEViewGONE = ((viewModelAgeInt10) ? (android.view.View.VISIBLE) : (android.view.View.GONE));
//數(shù)據(jù)綁定
// ?咦,這個(gè)咋沒適配器呢。因?yàn)镈atabind,針對(duì)屬性有對(duì)應(yīng)setXXX方法會(huì)默認(rèn)調(diào)用其setXXX方法就好,無需提供Adapter
//那android:text,也有setText()呀,為啥上面有呢,因?yàn)楣俜接X得那個(gè)方法太簡(jiǎn)單了,所以給提供了更優(yōu)化的方法,以達(dá)到優(yōu)化目的
//也就是說默認(rèn)的會(huì)調(diào)用屬性對(duì)應(yīng)的setXXX方法,如果有適配器定制的話就調(diào)用定制的
this.tv3.setVisibility(viewModelAgeInt10ViewVISIBLEViewGONE);
Null合并運(yùn)算符
< android:text="@{viewModel.name??viewModel.lastName}" />

對(duì)應(yīng)的賦值代碼塊

//聲明變量
java.lang.String viewModelNameJavaLangObjectNullViewModelLastNameViewModelName = null;
//給變量賦值,本質(zhì)還是用了我們?nèi)\(yùn)算符,也就是這個(gè)就是個(gè)語法糖
viewModelNameJavaLangObjectNullViewModelLastNameViewModelName = ((viewModelNameJavaLangObjectNull) ? (viewModelLastName) : (viewModelName));
//給View填充數(shù)據(jù),講過了不啰嗦
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv2, viewModelNameJavaLangObjectNullViewModelLastNameViewModelName);
視圖引用
< android:text="@{tv3.text}" />

適用兩個(gè)組件取值一致的場(chǎng)景

androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv3, stringValueOfViewModelAge);
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv4, stringValueOfViewModelAge);

其實(shí)本質(zhì)是兩個(gè)組件使用了統(tǒng)一個(gè)數(shù)據(jù)變量

集合
<!-- 要手動(dòng)導(dǎo)入,主語xml里不支持‘<’符號(hào),要用&lt代替 -->
<import type="java.util.List"/>
<variable
           name="listdata"
           type="List&lt;String>" />
<!-- 使用 -->
<TextView
android:text="@{viewModel.list[1]}"/>

對(duì)應(yīng)代碼生成的代碼

import java.util.List;
java.util.List<java.lang.String> viewModelList = null;
java.lang.String viewModelList1 = null;
viewModelList = viewModel.getList();
if (viewModelList != null) {
                        // read viewModel.list[1]
                        viewModelList1 = getFromList(viewModelList, 1);
                    }
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView14, viewModelList1);
//這個(gè)方法是ViewDatabinding基類提供的方法,與之對(duì)應(yīng)還有其它集合類方法,用于獲取集合中某個(gè)索引值用
protected static <T> T getFromList(List<T> list, int index) {
        if (list == null || index < 0 || index >= list.size()) {
            return null;
        }
        return list.get(index);
    }
字符串字面量

這個(gè)就簡(jiǎn)單的說下使用,場(chǎng)景上就是咱們的值是在雙引號(hào)內(nèi)的,里面如果需要字面常量時(shí)不能再用雙引號(hào),要用單引號(hào);當(dāng)然如果外層用單引號(hào)內(nèi)層就可以用雙引號(hào)了,總之就是不能同時(shí)出現(xiàn)兩個(gè)雙引號(hào)

<!-- 外單內(nèi)雙 -->
< android:text='@{"寫死的值"}' />
<!-- 外雙內(nèi)單 -->
< android:text="@{map[`firstName`]}" />
//到了編譯后就是用的死值,不會(huì)為其生成變量
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv5, "寫死的值");
資源

主要是資源文件的動(dòng)態(tài)話

<string name="content">I am %s , age is %d</string>
< android:text='@{@string/content(viewModel.name,viewModel.age)}' />
java.lang.String tv6AndroidStringContentViewModelNameViewModelAge = null;
//可以看到本質(zhì)上還是調(diào)用了tv6.getResources().getString()的方法
tv6AndroidStringContentViewModelNameViewModelAge = tv6.getResources().getString(R.string.content, viewModelName, viewModelAge);
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv6, tv6AndroidStringContentViewModelNameViewModelAge);
方法引用
事件處理的一種方式,官方的解釋是:
  • 在表達(dá)式中,您可以引用符合監(jiān)聽器方法簽名的方法。當(dāng)表達(dá)式求值結(jié)果為方法引用時(shí),數(shù)據(jù)綁定會(huì)將方法引用和所有者對(duì)象封裝到監(jiān)聽器中,并在目標(biāo)視圖上設(shè)置該監(jiān)聽器。如果表達(dá)式的求值結(jié)果為 null,則數(shù)據(jù)綁定不會(huì)創(chuàng)建監(jiān)聽器,而是設(shè)置 null 監(jiān)聽器。
  • 事件可以直接綁定到處理腳本方法,類似于為 Activity 中的方法指定 android:onClick 的方式。與 ViewonClick` 特性相比,一個(gè)主要優(yōu)點(diǎn)是表達(dá)式在編譯時(shí)進(jìn)行處理,因此,如果該方法不存在或其簽名不正確,則會(huì)收到編譯時(shí)錯(cuò)誤。
  • 方法引用和監(jiān)聽器綁定之間的主要區(qū)別在于實(shí)際監(jiān)聽器實(shí)現(xiàn)是在綁定數(shù)據(jù)時(shí)創(chuàng)建的,而不是在事件觸發(fā)時(shí)創(chuàng)建的。如果您希望在事件發(fā)生時(shí)對(duì)表達(dá)式求值,則應(yīng)使用監(jiān)聽器綁定

好難懂是吧,那我們接下來就根據(jù)栗子和源碼去理解吧

首先定義一個(gè)方法引用,如下
class ListenerHandler {
    fun tvOnClick(view: View){
        Toast.makeText(view.context,"aaaa",Toast.LENGTH_LONG).show()
    }
}

這個(gè)啥特點(diǎn)呢就是方法的參數(shù)與返回值必須與對(duì)應(yīng)事件的參數(shù)類型一致,方法名隨意,對(duì)應(yīng)到官方的一句話就是”引用符合監(jiān)聽器方法簽名的方法“

<variable
            name="clickHandler"
            type="org.geekbang.databindingtest.ListenerHandler" />

來個(gè)錯(cuò)誤的示范
fun tvOnClick(view: View) --> fun tvOnClick(context: Context);也就是方法簽名搞錯(cuò)了,會(huì)咋樣呢



會(huì)直接有個(gè)紅線,提示錯(cuò)了,也就是不和規(guī)范在編譯器就不行了
修正過來的寫法

android:onClick="@{clickHandler::tvOnClick}"

那么最終編譯出來的相關(guān)代碼是啥呢

//老樣子,根據(jù)文件里的東東生成一個(gè)變量
android.view.View.OnClickListener clickHandlerTvOnClickAndroidViewViewOnClickListener = null;
private OnClickListenerImpl mClickHandlerTvOnClickAndroidViewViewOnClickListener;
if (clickHandler != null) {
  clickHandlerTvOnClickAndroidViewViewOnClickListener = (((mClickHandlerTvOnClickAndroidViewViewOnClickListener == null) 
       ? (mClickHandlerTvOnClickAndroidViewViewOnClickListener = new OnClickListenerImpl()) 
       : mClickHandlerTvOnClickAndroidViewViewOnClickListener).setValue(clickHandler));
}

翻譯下就是,如果mXXX==null,則new OnClickListenerImpl(),否則mXXX.setValue(clickHandler)更新下值
先看否則 clickHandler,很明顯就是我們?cè)诓季治募械?lt;variable name="clickHandler">
核心還是那個(gè)OnClickListenerImpl,看下源碼

public static class OnClickListenerImpl implements android.view.View.OnClickListener{
        private org.geekbang.databindingtest.ListenerHandler value;
        public OnClickListenerImpl setValue(org.geekbang.databindingtest.ListenerHandler value) {
            this.value = value;
            return value == null ? null : this;
        }
        @Override
        public void onClick(android.view.View arg0) {
            this.value.tvOnClick(arg0); 
        }
    }

這個(gè)類能解釋好多官方解釋

  1. 這個(gè)類和實(shí)例是編譯時(shí)就創(chuàng)建,對(duì)應(yīng)官方的話:方法引用和監(jiān)聽器綁定之間的主要區(qū)別在于實(shí)際監(jiān)聽器實(shí)現(xiàn)是在綁定數(shù)據(jù)時(shí)創(chuàng)建的
  2. 這個(gè)類里持有一個(gè)變量org.geekbang.databindingtest.ListenerHandler value,這個(gè)變量的類型是我們的自定義的實(shí)現(xiàn)的類型,值也很明顯就是我們聲明的那個(gè)clickHandler;這里對(duì)應(yīng)官方的話:數(shù)據(jù)綁定會(huì)將方法引用和所有者對(duì)象封裝到監(jiān)聽器中
  3. 接口的實(shí)現(xiàn)最終調(diào)用的是value對(duì)應(yīng)的方法;這也就能解釋通為啥定義的方法一定要符合監(jiān)聽器的方法簽名了,也就是參數(shù)上要對(duì)應(yīng)好,從這里我們可以發(fā)現(xiàn),其實(shí)不一定參數(shù)類型完全一致,只要是事件方法參數(shù)的子類類型就可以了,不過一般設(shè)置接口時(shí)就會(huì)根據(jù)依賴倒置規(guī)則確定了類型上不能再具體了。

最后肯定是設(shè)置值了,這一些列操作編譯時(shí)相當(dāng)于都把代碼給我們寫好了,至此所謂的方法引用方式綁定事件處理就通了

this.tv1.setOnClickListener(clickHandlerTvOnClickAndroidViewViewOnClickListener);
監(jiān)聽器綁定
也是事件處理的一種方式,官方的解釋來一波:
  • 這些是在事件發(fā)生時(shí)進(jìn)行求值的 lambda 表達(dá)式。數(shù)據(jù)綁定始終會(huì)創(chuàng)建一個(gè)要在視圖上設(shè)置的監(jiān)聽器。事件被分派后,監(jiān)聽器會(huì)對(duì) lambda 表達(dá)式進(jìn)行求值。
  • 監(jiān)聽器綁定是在事件發(fā)生時(shí)運(yùn)行的綁定表達(dá)式。它們類似于方法引用,但允許您運(yùn)行任意數(shù)據(jù)綁定表達(dá)式。
  • 在方法引用中,方法的參數(shù)必須與事件監(jiān)聽器的參數(shù)匹配。在監(jiān)聽器綁定中,只有您的返回值必須與監(jiān)聽器的預(yù)期返回值相匹配(預(yù)期返回值無效除外)

定義,聲明,使用

class ListenerHandler {
   fun onClickByInfo(view:View,text:CharSequence){
        Toast.makeText(view.context,text,Toast.LENGTH_LONG).show()
    }
}
<variable
           name="clickHandler"
           type="org.geekbang.databindingtest.ListenerHandler" />
< android:onClick="@{(view)->clickHandler.onClickByInfo(view,viewModel.name)}" />

理一下生成的相關(guān)代碼,我們倒著看比較好,這里倒著看下

//給tv設(shè)置監(jiān)聽,這里的callBack肯定是一個(gè)OnClickListener
this.tv2.setOnClickListener(mCallback1);
// 果不其然,直接就是
@Nullable
 private final android.view.View.OnClickListener mCallback1;
// 那他賦值是誰呢,看下面,這個(gè)有兩個(gè)參數(shù),傳了this->ActivityMainBinding,還有一個(gè)1?,這個(gè)1是干啥的。。。現(xiàn)在不清楚
mCallback1 = new org.geekbang.databindingtest.generated.callback.OnClickListener(this, 1);
//看看這個(gè)類的實(shí)現(xiàn)
package org.geekbang.databindingtest.generated.callback;
public final class OnClickListener implements android.view.View.OnClickListener {
    final Listener mListener;
    final int mSourceId;
    public OnClickListener(Listener listener, int sourceId) {
        mListener = listener;
        mSourceId = sourceId;
    }
    @Override
    public void onClick(android.view.View callbackArg_0) {
        //我們點(diǎn)擊按鈕時(shí)會(huì)調(diào)這里,這個(gè)的具體實(shí)現(xiàn)交給了內(nèi)部接口實(shí)例mListener的_internalCallbackOnClick方法去實(shí)現(xiàn)了
        mListener._internalCallbackOnClick(mSourceId , callbackArg_0);
    }
    public interface Listener {
        void _internalCallbackOnClick(int sourceId , android.view.View callbackArg_0);
    }
}
接下來就看那個(gè)接口的實(shí)現(xiàn)在哪里,通過開始的賦值也能才到,實(shí)現(xiàn)在ActivityMainBinding,那么就看具體實(shí)現(xiàn)
public final void _internalCallbackOnClick(int sourceId , android.view.View callbackArg_0) {
        boolean clickHandlerJavaLangObjectNull = false;
        java.lang.String viewModelName = null;
        org.geekbang.databindingtest.ListenerHandler clickHandler = mClickHandler;
        org.geekbang.databindingtest.MainViewModel viewModel = mViewModel;
        boolean viewModelJavaLangObjectNull = false;
        clickHandlerJavaLangObjectNull = (clickHandler) != (null);
        if (clickHandlerJavaLangObjectNull) {
            viewModelJavaLangObjectNull = (viewModel) != (null);
            if (viewModelJavaLangObjectNull) {
                viewModelName = viewModel.getName();
                //上面都是變量聲明和檢測(cè),其實(shí)可以看出來就是各種非空邏輯的判斷,要確保不出現(xiàn)空指針
               //這里最終調(diào)用了我們定義的方法
                clickHandler.onClickByInfo(callbackArg_0, viewModelName);
            }
        }
    }

從上面源碼看,相比方法引用其實(shí)就是換了下寫法,同時(shí)支持非簽名參數(shù)了而已,因?yàn)檫@些代碼也都是編譯時(shí)都設(shè)置好了。
那么這兩種看都是將原來的onClick的具體實(shí)現(xiàn)最終轉(zhuǎn)給了我們自己寫的業(yè)務(wù)塊,只是方法參數(shù)上監(jiān)聽器比方法引用更加靈活,這里可以推斷出場(chǎng)景選擇,點(diǎn)擊事件不依賴于數(shù)據(jù)邏輯時(shí)用方法引用就好,如果依賴于數(shù)據(jù),比如我們的Rv的Item里的點(diǎn)擊需要把Item的data帶出去,就可以用監(jiān)聽器引用了。

可觀察數(shù)據(jù)對(duì)象

這個(gè)其實(shí)在實(shí)際中都以LiveData代替了,這最終是如何運(yùn)行的,且看下回分解。我們就通過一個(gè)簡(jiǎn)單的LiveData實(shí)例看看有啥相關(guān)代碼。

val descriptionInfo = MutableLiveData("簡(jiǎn)介")
< android:text="@={viewModel.descriptionInfo}" />

倒著看看相關(guān)代碼

//賦值
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tvDes, viewModelDescriptionInfoGetValue);
//相關(guān)變量聲明,能看到有兩個(gè)變量,一個(gè)是值,一個(gè)是LiveData
androidx.lifecycle.MutableLiveData<java.lang.String> viewModelDescriptionInfo = null;
java.lang.String viewModelDescriptionInfoGetValue = null;
//值肯定通過LiveData獲取的
if (viewModelDescriptionInfo != null) {
    viewModelDescriptionInfoGetValue = viewModelDescriptionInfo.getValue();
}
// LiveData通過變量賦值
if (viewModel != null) {
   // read viewModel.descriptionInfo
   viewModelDescriptionInfo = viewModel.getDescriptionInfo();
 }
//賦值后還有個(gè)下面的方法,從這里順下去應(yīng)該能找出是如何響應(yīng)LiveData變化的
 updateLiveDataRegistration(1, viewModelDescriptionInfo);
 /**
  * 更新liveData的的注冊(cè)器
  * 本地字段id
  * LiveData自己,算是一個(gè)observable
  */
protected boolean updateLiveDataRegistration(int localFieldId, LiveData<?> observable) {
        mInLiveDataRegisterObserver = true;
        try {
            //最終給了updateRegistration,參數(shù)CREATE_LIVE_DATA_LISTENER,這個(gè)方法最其實(shí)就是經(jīng)過關(guān)聯(lián)判斷,合理的去給LiveData去更新下觀察者,就不展開了
            return updateRegistration(localFieldId, observable, CREATE_LIVE_DATA_LISTENER);
        } finally {
            mInLiveDataRegisterObserver = false;
        }
    }
  //CREATE_LIVE_DATA_LISTENER是啥?可以看到其本質(zhì)是LiveDataListener,是ViewDataBinding的靜態(tài)常量。那就瞅一眼,有點(diǎn)多,我們只關(guān)注下核心的
//implements Observer是一個(gè)觀察者
private static class LiveDataListener implements Observer,
            ObservableReference<LiveData<?>> {
        //一個(gè)監(jiān)聽相關(guān)的數(shù)據(jù)包裝類,將各個(gè)參數(shù)存到這里面了
        final WeakListener<LiveData<?>> mListener;
            //略...
            mListener = new WeakListener(binder, localFieldId, this, referenceQueue);
           //略...
           //會(huì)給LiveData設(shè)置監(jiān)聽
           liveData.observe(newOwner, this);
           //略...
        //響應(yīng)監(jiān)聽變化
        public void onChanged(@Nullable Object o) {
            ViewDataBinding binder = mListener.getBinder();
            if (binder != null) {
                binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
            }
        }
    }

從上線我們能看到當(dāng)生性周期狀態(tài)變化后,會(huì)交由ViewDataBinding的handleFieldChange去響應(yīng)變化,接下來我們跟一下這條線的主要代碼

 //如果字段變化后會(huì)走requestRebind
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
        if (result) {
            requestRebind();
        }
 //requestRebind是請(qǐng)求重新綁定的意思,最終繞來繞去會(huì)走到executeBindings();而executeBindings是執(zhí)行綁定的意思,最終會(huì)回到我們開始的賦值階段

從上面看,我們的LiveData數(shù)據(jù)的管理還是有點(diǎn)復(fù)雜的,主要是很多預(yù)制的東西,本質(zhì)上還是注冊(cè)觀察者和響應(yīng)觀察者而已,只是將這一套流程模板化了而已

綁定適配器
自己的理解
  • 這個(gè)官網(wǎng)講述的都已經(jīng)很詳細(xì)了,這里主要是做做總結(jié),加深下理解,沒興趣的可以直接看官網(wǎng)。
  • 綁定的實(shí)質(zhì)是設(shè)置控件的某個(gè)屬性,如setText是改變文字,setAlpha是改變透明度,那么要改變這個(gè)屬性可能需要一些邏輯去定制,比如setText,如果新舊值沒有變化就沒必要設(shè)置了,多刷新一次而已,那么適配器的本質(zhì)作用就是為設(shè)置屬性提供自定義實(shí)現(xiàn)方式,類似于RecycleView中我們將布局填充交給Adapter一樣,這里是我們將某個(gè)屬性的修改交給了一個(gè)Adapter而已。
適配器的定制原則
  1. 理論上任何屬性都可以定制
  2. 自動(dòng)選擇:有setter方法的都可以直接搞
    • 有屬性有對(duì)應(yīng)的setter方法,可以直接用;例如android:gravity="@{vm.gravity}"
    • 沒有屬性,但是有setter方法,可以通過app:xxx="xxx"方式使用;例如app:scrimColor="@{@color/scrim}"
  3. 指定自定義方法名稱:有屬性,但是沒有對(duì)應(yīng)的setter,但是呢有其它名稱的方法可以單獨(dú)修改這個(gè)屬性,可以將屬性及方法建立關(guān)聯(lián)即可。例如android:tint這個(gè)屬性,木有setTint方法,但是有setImageTintList方法是用來設(shè)置各個(gè)屬性的,我們通過一下方法進(jìn)行關(guān)鍵即可。示例:
    @BindingMethods(value = [
            BindingMethod(
                type = android.widget.ImageView::class,
                attribute = "android:tint",
                method = "setImageTintList")])
    
    其實(shí)這個(gè)有,但是很少,首先一般寫法都是用對(duì)應(yīng)setter方法,及時(shí)需要關(guān)聯(lián)的,大多數(shù)DataBinding框架已經(jīng)幫我們建立關(guān)聯(lián)了,發(fā)現(xiàn)木有時(shí)到androidx.databinding.adapters下找找看。
  4. 完全自定義:這個(gè)就是既沒有默認(rèn),也無法1v1關(guān)聯(lián)的場(chǎng)景了;例如我們有android:paddingLeft屬性,但沒有該屬性的sePaddingLeft,有改變的方法setPadding但這個(gè)哥們是四個(gè)參數(shù)是改四個(gè)padding值用的,也就是完全驢唇對(duì)不上馬嘴,就只能自定義了,這也是我們大多數(shù)場(chǎng)景了。
    • 這個(gè)主要是單參數(shù),組合參數(shù)的情況,官網(wǎng)給了明確的示例,并且官方庫也提供了很多封裝,比著葫蘆畫瓢就好。注意在Kotlin中比官方更簡(jiǎn)單點(diǎn),因?yàn)镵otlin可以用擴(kuò)展函數(shù)實(shí)現(xiàn),省了一個(gè)參數(shù)的聲明
    • @BindingAdapter 其實(shí)讀一下這個(gè)注解的源碼及注釋就一通百通了
對(duì)象轉(zhuǎn)換
  • 我們目標(biāo)上在綁定數(shù)據(jù)時(shí)需要確保與屬性的值類型一致,如我們gravity屬性需要一個(gè)int,我們給一個(gè)字符串類型的肯定不合適。但是某些邏輯下我們得到的可能并不是int類型,這就需要轉(zhuǎn)換了。轉(zhuǎn)換的目的是讓值能符合屬性設(shè)置的類型要求。
  • 自動(dòng)轉(zhuǎn)換:有些屬性可以支持多種類型,比如setText,可以是int的resId,可以是String類型,自動(dòng)轉(zhuǎn)換的理解就是可以根據(jù)給定的值類型自動(dòng)選擇調(diào)用哪個(gè)方法來設(shè)置屬性。
  • 自定義轉(zhuǎn)換:就是原始值不滿足屬性值的類型要求;舉兩個(gè)例子,backGround屬性需要Drawable,我們給@color/xxx肯性不行,還有一個(gè)較為常見的場(chǎng)景,我們一般拿到的時(shí)間戳,但是需要顯示的是xxxx年xx月xx日的格式。定義方法比較簡(jiǎn)單,就是在方法上加個(gè)@BindingConversion就可以了。咱們用個(gè)例子看下
    // 我們有一個(gè)user對(duì)象
    val user = User("二胖胖",18)
    
    <!-- 給text直接指定了對(duì)象了,理論上我們木有setText(user:User)類型的方法的 -->
    < android:text="@{viewModel.user}" />
    
    我們定義個(gè)轉(zhuǎn)義器
    @BindingConversion
    fun convertUserToString(user: User): String? {
        return user.toString()
    }
    
    看下最終表現(xiàn)
    androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv9, org.geekbang.databindingtest.ViewExBindingAdaptersKt.convertUserToString(viewModelUser));
    
    也就是編譯器會(huì)發(fā)現(xiàn)類型不匹配會(huì)找對(duì)應(yīng)的covert方法來用
雙向綁定
  • 首先這個(gè)東西看官網(wǎng)得看幾遍,個(gè)人覺得不是很好理解
  • 其實(shí)掌握以下就可以了
    1. 這個(gè)的應(yīng)用場(chǎng)景就是,某個(gè)UI依賴一個(gè)數(shù)據(jù)展示,這個(gè)UI的內(nèi)容變化后會(huì)改變這個(gè)數(shù)值;例如EditText,我們初始值依賴一個(gè)變量value,隨著輸入的變化輸入框的值會(huì)實(shí)時(shí)改變value的值,翻譯過來就是通過簡(jiǎn)單的寫法可以實(shí)現(xiàn)如下兩個(gè)操作:
       editText.text = value
       editText.addTextChangedListener(object : TextWatcher(){
            override fun afterTextChanged(s: Editable?) {
                value = s.toString()
            }
        })
      
    2. 寫法的話就是語法糖@=
      < android:text="@={value}" />
      
    3. 系統(tǒng)提供了大多數(shù)使用場(chǎng)景雙向特性
    4. 想自定義的話,需要掌握幾個(gè)注解用法 @BindingAdapter @InverseBindingAdapter @InverseBindingMethods
ViewStub
  • 額...沒咋用過...也就沒深入研究了,但是這個(gè)是布局優(yōu)化的一個(gè)點(diǎn),有興趣深究下就好
include
  • 本質(zhì)只是一個(gè)DataBinding的包裹,掌握傳值就好了,不過這里寫演示實(shí)例時(shí)遇到個(gè)坑,就是在約束布局里的寬高用wrapper不行,了解下就好了。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,786評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,964評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,354評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,554評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,106評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,918評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,093評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,342評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評(píng)論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,839評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,107評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容