Android DataBinding自定義數(shù)據(jù)雙向綁定 筆記

在學(xué)習(xí)dataBinding的數(shù)據(jù)雙向綁定時(shí),針對(duì)自定義特性的雙向數(shù)據(jù)綁定看的一頭霧水,現(xiàn)在有了大致了解,記錄一下

1、XML中使用方式:

單向數(shù)據(jù)綁定

    <CheckBox
        android:id="@+id/rememberMeCheckBox"
        android:checked="@{viewmodel.rememberMe}"
        android:onCheckedChanged="@{viewmodel.rememberMeChanged}"
    />

雙向數(shù)據(jù)綁定,在單向上加上=

    <CheckBox
        android:id="@+id/rememberMeCheckBox"
        android:checked="@={viewmodel.rememberMe}"
    />

相信大家對(duì)于如何在xml總定義雙向數(shù)據(jù)綁定還是很清楚,但是對(duì)于如何在代碼中使用及操作就有點(diǎn)懵了,下面說(shuō)下流程。

2、代碼中使用方式(分2種情況)

情況一、數(shù)據(jù)綁定是使用在View的系統(tǒng)屬性上,且view中有提供setter方法

    <CheckBox
        android:id="@+id/rememberMeCheckBox"
        android:checked="@={viewmodel.rememberMe}"http://android:checked屬性是CheckBox類中有的屬性,且類中有提供setter方法
    />

情況二、view有相關(guān)屬性,但是類中沒(méi)有提供setter方法,或數(shù)據(jù)綁定是使用在View的自定義屬性上

    <CheckBox
        android:id="@+id/rememberMeCheckBox"
        android:paddingLeft="@={viewmodel.paddingLeft}"http://android:paddingLeft屬性是CheckBox類中有的屬性,但是沒(méi)有提供setter方法
    />
    
    <EditText
        android:id="@+id/time"
        app:myText="@={viewmodel.myText}"http:// app:time屬性在EditText中不存在
    />

2.1 情況一(數(shù)據(jù)是綁定在系統(tǒng)屬性上的)

? 針對(duì)這種使用的是view存在的屬性且view中有提供setter方法,只需要在數(shù)據(jù)類ViewModel類上做操作就可以了,很簡(jiǎn)單

    public class ViewModel extends BaseObservable {
        private boolean rememberMe;
        private String text;

        @Bindable
        public Boolean getRememberMe() {
            return rememberMe;
        }

        public void setRememberMe(Boolean value) {
            // 避免死循環(huán).
            if (rememberMe != value) {
                rememberMe = value;
                // 通知界面更新.
                notifyPropertyChanged(BR.remember_me);
            }
        }
        
        @Bindable
        public String getText() {
            return text;
        }

        public void setText(String text) {
            Log.d(TAG, "情況1:源數(shù)據(jù)更改,代碼中調(diào)用這個(gè)setter方法\n情況2:用戶改變界面的值,觸發(fā)反向設(shè)置值步驟3,在onChange中調(diào)用@Bindable修飾過(guò)的屬性的setter方法,設(shè)置最新值");
            if (TextUtils.equals(text, this.text)) {
                return;
            }
            this.text = text;
            notifyPropertyChanged(BR.text);
        }     
    }
  • ? 首先讓數(shù)據(jù)類繼承BaseObservable,這樣數(shù)據(jù)類就具有了在數(shù)據(jù)更新時(shí),可以通知界面的方法notifyPropertyChanged(BR.xx);

  • ? 針對(duì)要操作的綁定屬性rememberMe,提供setter和getter方法;

  • ? 然后在屬性的getter方法上添加注解@Bindable,系統(tǒng)會(huì)在BR類中生成一個(gè)條目BR.remember_me,下次如果rememberMe的值更新時(shí),調(diào)用notifyPropertyChanged(BR.remember_me)就可以使界面更新;

  • 當(dāng)界面用戶操作導(dǎo)致CheckBoxcheck變動(dòng)時(shí),系統(tǒng)會(huì)調(diào)用提供的setter方法。

2.2 情況二(數(shù)據(jù)是綁定在自定義屬性上的)

? 數(shù)據(jù)類ViewModel類的設(shè)置,和情況一的一樣設(shè)置,記得參考上面情況一設(shè)置數(shù)據(jù)類。

? 情況二,還需要針對(duì)xml中自定義的屬性,添加解釋類和方法

2.2.1 先說(shuō)幾個(gè)個(gè)人理解,方便后續(xù)說(shuō)明

(1)如果我們?cè)趚ml中使用自定義屬性,在當(dāng)前view中不存在setter方法,例如:

    <EditText
        android:id="@+id/time"
        app:myText="@={viewmodel.text}"
    />

? 很明顯app:myText=這個(gè)屬性,EditText中是不存在setMyText()方法的,出現(xiàn)這種情況的時(shí)候,編譯器編譯時(shí)就會(huì)報(bào)錯(cuò),解決方法就是,告訴數(shù)據(jù)綁定模塊,如果沒(méi)有setter方法時(shí),應(yīng)該使用我們自己提供的方法。

(2)@BindingAdapter("屬性名"),

當(dāng)給方法添加這個(gè)注解后,表示正向綁定時(shí),設(shè)置的值應(yīng)該通過(guò)這個(gè)方法操作,

    // 負(fù)責(zé)解析xml中app:myText的屬性,并調(diào)用下面這個(gè)方法
    @BindingAdapter("app:myText")
    public static void setText(EditText view, String text) {
        if (TextUtils.equals(text,"1")) {
            view.setBackground(new ColorDrawable(0xffff0000));
        }else {
            view.setBackground(new ColorDrawable(0xff0000ff));
        }
    }

(3)@InverseBindingAdapter("屬性名")

當(dāng)給方法添加這個(gè)注解后,表示反向綁定時(shí),會(huì)調(diào)用這個(gè)方法,

    @InverseBindingAdapter(attribute = "app:myText")
    public static String getText(EditText view) {
        return view.getText().toString();
    }

(4)當(dāng)我們?cè)趚ml中使用

    <EditText
        android:id="@+id/time"
        app:myText="@={viewmodel.text}"
    />

? 時(shí)

app:myText="@={viewmodel.text}"

系統(tǒng)(編譯器,后續(xù)統(tǒng)稱為系統(tǒng)吧)其實(shí)為我們分解成了下面這兩部分

app:myText="@{viewmodel.text}"
app:myTextAttrChanged="@{inverseBindingListener}"http://inverseBindingListener是系統(tǒng)生成的,我們現(xiàn)在不用管它是哪兒來(lái)的

看到上面這2個(gè)屬性,大家應(yīng)該就明白了,類似我們開(kāi)頭數(shù)據(jù)單向綁定,

app:myText="@{viewmodel.text}"是用來(lái)做數(shù)據(jù)正向綁定的,app:myTextAttrChanged="@{inverseBindingListener}"是用來(lái)做數(shù)據(jù)反向綁定的,一個(gè)回調(diào)對(duì)象

2.2.2 步驟及解釋

? 通過(guò)2.2.1第4點(diǎn)的說(shuō)明,我們看到系統(tǒng)幫我們生成的app:myTextapp:myTextAttrChanged在EditText中是不存在的屬性,肯定也沒(méi)有setter方法,所以根據(jù)2.2.1第1點(diǎn),此時(shí)就需要我們自定義方法,來(lái)讓系統(tǒng)使用我們提供的方法

? a.先解決數(shù)據(jù)正向綁定的問(wèn)題,用2.2.1第2點(diǎn)的注解便可:

    // 負(fù)責(zé)解析xml中app:myText的屬性,并調(diào)用下面這個(gè)方法
    @BindingAdapter("app:myText")
    public static void setText(EditText view, String text) {
        if (TextUtils.equals(text,"1")) {
            view.setBackground(new ColorDrawable(0xffff0000));
        }else {
            view.setBackground(new ColorDrawable(0xff0000ff));
        }
    }

    // 負(fù)責(zé)解析xml中app:myTextAttrChanged的屬性,并調(diào)用下面這個(gè)方法
    @BindingAdapter("app:myTextAttrChanged")
    public static void setListener(EditText view, final InverseBindingListener listener)    {
        //內(nèi)容需要在反向綁定數(shù)據(jù)時(shí),再寫(xiě)。
    }
    

? 通過(guò)上面這兩個(gè)方法,算是完成了數(shù)據(jù)的正向綁定,

? app:myText的方法,其實(shí)就是判斷給定的text是否等于1,然后給EditText設(shè)置不同的背景色;

? app:myTextAttrChanged的方法,就是告訴系統(tǒng)什么時(shí)候,觸發(fā)回調(diào);

? b.數(shù)據(jù)反向綁定:

? 反向綁定其實(shí),就是把用戶在界面的改變,設(shè)置回我們自己的數(shù)據(jù)類上,本例中,就是把用戶輸入的文本,設(shè)置回viewmodeltext屬性上

? 由于我們已經(jīng)給數(shù)據(jù)類ViewModel中的text屬性添加了@Bindable注解,并且上面提到的系統(tǒng)自動(dòng)生成的InverseBindingListener中的onChange()方法是會(huì)幫我們調(diào)用text屬性的setter方法,所以我們只需要告訴數(shù)據(jù)綁定系統(tǒng)在什么時(shí)候設(shè)置和需要設(shè)置什么值便可。

? ① 在什么時(shí)候設(shè)置,由于是當(dāng)用戶輸入時(shí),我們就要同步獲取輸入內(nèi)容,所以我們肯定是要在EditText上添加一個(gè)文本變化監(jiān)聽(tīng)器,用戶只要一輸入,我們通過(guò)監(jiān)聽(tīng)器就能馬上知道,所以我們可以在我們自定義的app:myTextAttrChanged方法中這樣寫(xiě):

    // 負(fù)責(zé)解析xml中app:myTextAttrChanged的屬性,并調(diào)用下面這個(gè)方法
    @BindingAdapter("app:myTextAttrChanged")
    public static void setListener(EditText view, final InverseBindingListener listener) {
        if (listener != null) {
            view.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                }

                @Override
                public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                    Log.d(TAG, "用戶改變界面的值,觸發(fā)反向設(shè)置值步驟1,準(zhǔn)備調(diào)用自動(dòng)生成的onChange");
                    listener.onChange();
                }

                @Override
                public void afterTextChanged(Editable editable) {

                }
            });
        }
    }

? 當(dāng)EditText內(nèi)容有變化時(shí),就會(huì)調(diào)用系統(tǒng)生成的InverseBindingListeneronChange()方法,然后系統(tǒng)在onChange()方法中就會(huì)自動(dòng)把值設(shè)置給數(shù)據(jù)類對(duì)象ViewModel,至于值從哪兒來(lái),我們看下面的②;

? ② 需要設(shè)置什么值,其實(shí)在InverseBindingListeneronChange()方法中,系統(tǒng)還會(huì)調(diào)用@InverseBindingAdapter("屬性名")修飾的方法,即2.2.1第3點(diǎn)的方法,所以我們只需要把我們要設(shè)置的值,通過(guò)這個(gè)方法返回便可:

    @InverseBindingAdapter(attribute = "app:myText")
    public static String getText(EditText view) {
        return view.getText().toString();
    }

? 至此便完成了,基本的數(shù)據(jù)雙向綁定。

3、流程梳理

前提:

數(shù)據(jù)對(duì)象:

public class MyViewModel extends BaseObservable {
    private static final String TAG = "MyObservable";
    private String text;

    @Bindable
    public String getText() {
        return text;
    }

    public void setText(String text) {
        if (TextUtils.equals(text, this.text)) {
            return;
        }
        this.text = text;
        notifyPropertyChanged(BR.text);
    }
}

xml文件:

    <EditText
        android:id="@+id/time"
        app:myText="@={viewmodel.text}"
    />

數(shù)據(jù)正向綁定流程:MyObservable.text屬性變化,調(diào)用屬性的setter方法,觸發(fā)notifyPropertyChanged(BR.text),根據(jù)xml中使用,會(huì)調(diào)用@BindingAdapter("app:myText")注解的方法;

數(shù)據(jù)反向綁定流程:EditText內(nèi)容變化時(shí),觸發(fā)InverseBindingListener中的onChange()方法,onChange()中會(huì)調(diào)用獲取值的@InverseBindingAdapter(attribute = "app:myText")注解的方法,然后onChange()中會(huì)調(diào)用MyObservable.text屬性的setter方法,觸發(fā)notifyPropertyChanged(BR.text),根據(jù)xml中使用,會(huì)調(diào)用@BindingAdapter("app:myText")注解的方法;

4、后話

? 其實(shí)數(shù)據(jù)綁定系統(tǒng),也有自己實(shí)現(xiàn)的類,大家可以參考下:androidx.databinding.adapters.ViewBindingAdapter。

? 當(dāng)然還有@BindingMethods@InverseBindingMethods,以及更高深的用法,我現(xiàn)在還在學(xué)習(xí)中,后續(xù)有時(shí)間會(huì)繼續(xù)更新

? 由于摻雜很多個(gè)人的理解的,文中描述不一定完全正確,歡迎留言指正和討論。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容