Android中的MVVM DataBinding指南(二)

之前介紹了如何使用 DataBinding 骚灸,其中涉及到ViewModel的定義甚牲,這期我們就詳細解析一下如何定義及使用ViewModel

Observable Objects

DataBinding 中有個接口Observable蝶柿,實現(xiàn)這個接口可以添加一個監(jiān)聽器來綁定觀察類中的屬性是否有變化。有個實現(xiàn)了Observable的父類BaseObservable雏赦,由 DataBinding 提供芙扎,代碼如下

public class BaseObservable implements Observable {
    private transient PropertyChangeRegistry mCallbacks;

    public BaseObservable() {
    }

    @Override
    public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
        if (mCallbacks == null) {
            mCallbacks = new PropertyChangeRegistry();
        }
        mCallbacks.add(callback);
    }

    @Override
    public synchronized void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
        if (mCallbacks != null) {
            mCallbacks.remove(callback);
        }
    }

    public synchronized void notifyChange() {
        if (mCallbacks != null) {
            mCallbacks.notifyCallbacks(this, 0, null);
        }
    }

    public void notifyPropertyChanged(int fieldId) {
        if (mCallbacks != null) {
            mCallbacks.notifyCallbacks(this, fieldId, null);
        }
    }
}

可以看到這個類幫我們實現(xiàn)了Observable接口里的方法戒洼,并且添加了notifyChange()notifyPropertyChanged(int fieldId)方法供子類調(diào)用圈浇。這里會讓人聯(lián)想到JDK中的java.util.Observable靴寂,它是個類不是接口召耘,也沒有實現(xiàn)任何接口怎茫,這個設(shè)計有點糟糕,因為Java是不支持多繼承的蜜宪,如果子類需要java.util.Observable和另一個超類的行為祥山,就會陷入兩難境界。DataBinding 的設(shè)計有效的避免了這種問題澳窑,它提供一個接口定義了規(guī)范供常,并提供一個實現(xiàn)了接口的超類供繼承栈暇,如果覺得BaseObservable占了繼承位也可以自己定義超類實現(xiàn)Observable接口。
?現(xiàn)在我們重新觀察BaseObservable這個類煎源,notifyPropertyChanged(int fieldId)是一個非常有用的方法香缺,在子類的setter中調(diào)用,能夠更新View中的數(shù)據(jù)锋拖。例如

public class MainViewModel extends BaseObservable {
    private String phone;

    public MainViewModel(String phone) {
        this.phone = phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
        notifyPropertyChanged(BR.phone);
    }

    @Bindable
    public String getPhone() {
       return phone;
   }

上面的MainViewModel中的getter加入注解@Bindable兽埃,這樣會在編譯時在BR中生成一個標識倔撞,這樣能夠鑒定這個屬性是否被修改過痪蝇。BR是編譯時生成的類似于Android中的R.class的文件,其中也是標識了所有你在DataBinding 中定義的類和屬性趁矾。而在自動生成的MainBinding.class中也印證了這一點给僵,當調(diào)用mainBinding.setUser(user)MainBinding也有調(diào)用notifyPropertyChanged(BR.user)

public class MainBinding extends android.databinding.ViewDataBinding  {
  public void setUser(com.winter.huang.databinding.viewmodel.MainViewModel user) {
        updateRegistration(0, user);
        this.mUser = user;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        notifyPropertyChanged(BR.user);
        super.requestRebind();
    }
  }

顆粒度更小的ObservableField

如果覺得只需要部分數(shù)據(jù)需要進行數(shù)據(jù)綁定帝际,無需繼承這么麻煩,可以使用ObservableField 包括 ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, ObservableParcelable, ObservableArrayMap, ObservableArrayList具體可見

綁定事件

先定義一個ActionHandler類斑粱,定義兩個方法脯爪,用來體現(xiàn)綁定事件的兩種方式

public class ActionHandler {

    public void showPhone(MainViewModel viewModel) {
        Toast.makeText(AppApplication.getAppContent(), viewModel.getPhone(), Toast.LENGTH_SHORT).show();
    }

    public void showPhone(View view) {

    }
}

可以在xml中定義并使用

<import type="com.winter.huang.databinding.utils.ActionHandler" alias="ActionHandler"/>
<variable
         name="actionHandler"
         type="ActionHandler"/>

         <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="New Button"
            android:id="@+id/button"
            android:onClick="@{actionHandler.showPhone}"/>

上面這種方法會調(diào)用showPhone(View view)痕慢,DataBinding 會默認傳入當前View的實例進入方法體掖举,所以直接寫actionHandler.showPhone就可以。另一種用法是使用λ表達式拇泛。

<Button
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="New Button"
           android:id="@+id/button"
           android:onClick="@{() -> actionHandler.showPhone(user)}"/>

兩種方式是有本質(zhì)區(qū)別的俺叭,第一種方式和以往在xml中聲明android:onClick=showPhone,之后在Activity中定義是相同的。

public void showPhone(View view) {
       //do something
   }

第二種方式可以理解為此處使用了一個OnClickListener匿名內(nèi)部類的方式調(diào)用actionHandler.showPhone(user)蜈垮,通常情況推薦第二種攒发,因為點擊事件可能會引起數(shù)據(jù)的改變晋南,第二種方式直接傳入ViewModel,更方便操作數(shù)據(jù)偶妖。

@BindingAdapter 自定義屬性值

假如需要為EidtText設(shè)置TextWatcher,在xml中是不支持該屬性的态秧,這時我們可以自定義屬性扼鞋,使用@BindingAdapter注解云头,定義公有靜態(tài)方法傳入需要操作的View和其他參數(shù)即可,下面的代碼為EditText設(shè)置屬性android:afterTextChanged

@BindingAdapter({"android:afterTextChanged"})
    public static void showViewModel(final EditText editText, final MainViewModel viewModel) {
        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                if (!s.toString().equals(viewModel.getPhone())) {
                    viewModel.setPhone(s.toString());
                }
            }
        });

    }

由于DataBinding 默認第一個參數(shù)必須是View,而且可以缺省傳入竿痰,所以xml中可以寫為

<EditText
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:afterTextChanged="@{user}"/>

@BindingAdapter({"android:afterTextChanged"}) 中定義了命名空間和屬性名影涉,關(guān)于命名空間可以參考stackoverflow這個問題ianhanniballake的回答
對于相對復雜的交互匣缘,可能需要多個屬性值協(xié)作鲜棠,@BindingAdapter也支持傳入多個屬性,需要在定義時傳入數(shù)組即可

public class ActionHandler implements View.OnClickListener{

    @BindingAdapter({"android:onClick", "android:clickable"})
    public static void setOnClick(View view, View.OnClickListener onClickListener, boolean clickable) {
        view.setOnClickListener(onClickListener);
        view.setClickable(clickable);
    }

    @Override
    public void onClick(View v) {
        Toast.makeText(AppApplication.getAppContent(), "onClick", Toast.LENGTH_SHORT).show();
    }
}

xml

<Button
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:clickable="@{true}"
  android:onClick="@{actionHandler}" />

屬性的順序可以不依賴定義的順序,同樣View是默認傳入表鳍,此處每個屬性傳入一個參數(shù)即可祥诽,如果某個屬性未定義,則由默認值決定厘熟,如android:clickable="@{true}"不聲明,并不影響Button是否可以點擊颇玷。關(guān)于@BindingAdapter的工作原理可以參考這里

之后會嘗試寫一個基于DataBinding 的小項目就缆,如果遇到什么坑會及時分享出來竭宰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末切揭,一起剝皮案震驚了整個濱河市锁摔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌孕豹,老刑警劉巖十气,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砸西,死亡現(xiàn)場離奇詭異芹枷,居然都是意外死亡,警方通過查閱死者的電腦和手機饱溢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門蝶涩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绿聘,“玉大人,你說我怎么就攤上這事兽愤。” “怎么了逐沙?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵洼畅,是天一觀的道長帝簇。 經(jīng)常有香客問我,道長残揉,這世上最難降的妖魔是什么芋浮? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任纸巷,我火速辦了婚禮镇草,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瘤旨。我一直安慰自己陶夜,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布裆站。 她就那樣靜靜地躺著条辟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宏胯。 梳的紋絲不亂的頭發(fā)上羽嫡,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音肩袍,去河邊找鬼。 笑死氛赐,一個胖子當著我的面吹牛魂爪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播艰管,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼滓侍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了牲芋?” 一聲冷哼從身側(cè)響起撩笆,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤捺球,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后夕冲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氮兵,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年歹鱼,在試婚紗的時候發(fā)現(xiàn)自己被綠了泣栈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡弥姻,死狀恐怖秩霍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚁阳,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布鸽照,位于F島的核電站螺捐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏矮燎。R本人自食惡果不足惜定血,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诞外。 院中可真熱鬧澜沟,春花似錦、人聲如沸峡谊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽既们。三九已至濒析,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間啥纸,已是汗流浹背号杏。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留斯棒,地道東北人盾致。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像荣暮,于是被迫代替她去往敵國和親庭惜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

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