Android使用DataBinding實現(xiàn)雙向綁定(一)

前面一段時間學(xué)習(xí)了一下Android中的DataBinding橙弱,但是只是很簡單地實現(xiàn)了一下顿膨,DataBinding中最強大的地方還沒有認真地學(xué)習(xí)過萤皂,有很多地方還不理解。這次竭鞍,深入學(xué)習(xí)一下DataBinding的雙向綁定和MVVM模式板惑。

1、實現(xiàn)簡單的使用

先回顧一下簡單的使用笼蛛,使用的時候需要在模塊的build.gradle文件中添加這一句:

dataBinding{
    enabled=true
}

添加這一句之后洒放,gladle文件sync之后就可以開始使用。

接著我們開始搭建一個基于MVVM+DataBinding簡單的開發(fā)框架滨砍。

1.1、view層

首先是新建一個全局的View的接口和一個基類的BaseActivity,具體代碼如下:

//全局的接口
public interface IView {
    void setViewModel(BaseVM baseVM);
}

//基類的BaseActivity
public  abstract class BaseActivity extends AppCompatActivity implements IView{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
}

基類的activity在具體的項目中會封裝一些常用的方法惋戏,比如實現(xiàn)的activity的安全退出等领追,這里不是真實的項目,所以就沒有封裝那些方法响逢。

1.2绒窑、viewmodel層

上面搭建完成view層,接著是viewmodel層舔亭。同樣些膨,需要建一個全局的基類BaseVM,BaseVM的具體代碼如下:

public class BaseVM {

    public String getString(int resID) {
        return TestApplication.getContext().getString(resID);
    }
}

這里的這個方法是用于獲取資源文件里面的字符串钦铺,直接寫在這里订雾,我們需要在VM中使用的時候可以直接使用,方便矛洞。

1.3洼哎、model層

model層的話很多都是數(shù)據(jù)bean,這里我們新建一個簡單的bean沼本,這個bean只是簡單的封裝一些顯式信息噩峦,為了可以實現(xiàn)自動更新,我們需要做一些處理抽兆,具體的代碼如下:

public class UserBean extends BaseObservable {
    private String userCard;
    private String userName;
    private String userAddres;
    private String userEmail;

    public UserBean() {

    }

    public UserBean(String userCard, String userName, String userAddres, String userEmail) {
        this.userCard = userCard;
        this.userName = userName;
        this.userAddres = userAddres;
        this.userEmail = userEmail;

    }

    @Bindable
    public String getUserCard() {
        return userCard;
    }


    public void setUserCard(String userCard) {
        this.userCard = userCard;
        notifyPropertyChanged(cn.amos.BR.userCard);
    }

    @Bindable
    public String getUserName() {
        return userName;
    }


    public void setUserName(String userName) {
        this.userName = userName;
        notifyPropertyChanged(cn.amos.BR.userName);
    }

    @Bindable
    public String getUserAddres() {
        return userAddres;
    }


    public void setUserAddres(String userAddres) {
        this.userAddres = userAddres;
        notifyPropertyChanged(cn.amos.BR.userAddres);
    }

    @Bindable
    public String getUserEmail() {
        return userEmail;
    }


    public void setUserEmail(String userEmail) {
        this.userEmail = userEmail;
        notifyPropertyChanged(cn.amos.BR.userEmail);
    }

    @Override
    public String toString() {
        return "UserBean{" +
                "userCard=" + userCard +
                ", userName='" + userName + '\'' +
                ", userAddres='" + userAddres + '\'' +
                ", userEmail='" + userEmail + '\'' +
                '}';
    }
}

這里繼承了BaseObservable這個類识补,以及后面在Get方法上面添加@Bindable注解為了實現(xiàn)自動更新,也是需要在set方法中添加notifyPropertyChanged(cn.amos.BR.userEmail);這一句代碼辫红,這里是提醒更新數(shù)據(jù)凭涂。需要注意的是BR這個類,比較容易弄亂厉熟。

還做一個封裝就是DataBindingUtils导盅,看一下具體的代碼:

public class DataBindingUtils {
    private static ArrayMap<BaseActivity, ArrayMap<Observable, Observable.OnPropertyChangedCallback>> commonMap = new ArrayMap<>();

    public static void addCallBack(BaseActivity baseActivity,
                                   Observable observable, Observable.OnPropertyChangedCallback callback) {
        ArrayMap<Observable, Observable.OnPropertyChangedCallback> callbackArrayMap = commonMap.get(baseActivity);
        if (callbackArrayMap == null) {
            callbackArrayMap = new ArrayMap<>();
            commonMap.put(baseActivity, callbackArrayMap);
        }
        observable.addOnPropertyChangedCallback(callback);
        callbackArrayMap.put(observable, callback);
    }

    public static void removeCallBack(BaseActivity activity) {
        ArrayMap<Observable, Observable.OnPropertyChangedCallback> callbackArrayMap = commonMap.get(activity);
        if (callbackArrayMap != null) {
            for (Observable observable : callbackArrayMap.keySet()) {
                observable.removeOnPropertyChangedCallback(callbackArrayMap.get(observable));
            }
        }
    }
}

這里封裝了兩個方法,第一個是添加回調(diào)揍瑟,另外一個就是移除回調(diào)白翻,沒有什么很復(fù)雜的東西,需要解析一下的就是:Observable.OnPropertyChangedCallback绢片,這是一個當(dāng)observable中的屬性發(fā)生改變時由Observable調(diào)用的回調(diào)接口滤馍,需要實現(xiàn)的就是一個onPropertyChanged(Observable observable, int i)這個方法,兩個屬性底循,第一個是observable是正在改變的observable巢株,另外一個是需要用BR標(biāo)識,并且get方法上面需要添加@Bindable注解熙涤。需要了解這個API的阁苞,可以點擊這里

到這里困檩,基本就可以完成了框架的簡易搭建,當(dāng)然那槽,這里只是非常簡單的悼沿,在實際項目中還需要其他的東西,這里只是一個簡單的Demo骚灸,所以就簡單一點糟趾,接下來我們開始實現(xiàn)具體的業(yè)務(wù)。

2甚牲、實現(xiàn)按鈕的點擊

首先我們先實現(xiàn)一個簡單的按鈕點擊义郑,跳轉(zhuǎn)另外界面的一個需求。

首先是新建一個activity和對應(yīng)的XML文件丈钙,具體的代碼分別是:

public class MainActivity extends BaseActivity {
    private MainActivityVM mActivityVM;
    private ActivityMainBinding mDataBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivityVM = new MainActivityVM();
        setViewModel(mActivityVM);
        addCallBack();
    }

    private void addCallBack() {
        DataBindingUtils.addCallBack(this, mActivityVM.goToSimple, new Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(Observable observable, int i) {
                goToBase();
            }
        });
    }

    private void goToBase() {
        startActivity(new Intent(MainActivity.this, SimpleActivity.class));
    }

    @Override
    public void setViewModel(BaseVM baseVM) {
        this.mActivityVM = (MainActivityVM) baseVM;
        mDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mDataBinding.setMainActivityVM(mActivityVM);
    }
}

這里的activity需要繼承BaseActivity非驮,然后需要實現(xiàn)setViewModel(BaseVM baseVM)這個方法,這個是在IView這個接口中的著恩,每個繼承于BaseActivity的都需要實現(xiàn)這個方法院尔,這個方法就是設(shè)置一個VM的。

然后是XML的代碼:

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <import type="cn.amos.vm.MainActivityVM"/>

        <variable
            name="mainActivityVM"
            type="MainActivityVM"/>
    </data>

    <RelativeLayout
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="cn.amos.view.MainActivity">

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{mainActivityVM.goToNext}"
            android:text="簡單實現(xiàn)綁定數(shù)據(jù)"/>
    </RelativeLayout>
</layout>

這里需要一個MainActivityVM喉誊,這個類的代碼是:

public class MainActivityVM extends BaseVM {

    public ObservableInt goToSimple = new ObservableInt();

    public void goToNext(View view) {
        goToSimple.notifyChange();
    }
}

這里就一個方法邀摆,這個方法GoToNext,這個是button的點擊事件伍茄,注意需要傳入一個View的參數(shù)栋盹。這里調(diào)用了 goToSimple.notifyChange();這個方法,會在MainActivity中實現(xiàn)回調(diào)敷矫,我們在MainActivity中有一個addCallBack的方法中添加了這個回調(diào)的監(jiān)聽例获。

public ObservableInt goToSimple = new ObservableInt();

這個也是實現(xiàn)數(shù)據(jù)更新的,不過比實現(xiàn)BaseObservable這個類更加細曹仗。

這樣就可以實現(xiàn)點擊跳轉(zhuǎn)了榨汤。

3、實現(xiàn)一些簡單信息更新

新建一個SimpleActivity怎茫、對應(yīng)的XML文件和SImpleActivityVM收壕,具體的代碼是:

首先是SimpleActivity的代碼:

public class SimpleActivity extends BaseActivity {
    private static final String TAG = "SimpleActivity";
    private SimpleActivityVM mSimpleActivityVM;
    private ActivitySimpleBinding mSimpleBinding;

    private Toolbar mTbSimple;
    private UserBean mUserBean = new UserBean();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSimpleActivityVM = new SimpleActivityVM();
        setViewModel(mSimpleActivityVM);
        addCallBack();
        initUi();
    }

    private void initUi() {
        mTbSimple = mSimpleBinding.tbSimple;
        setSupportActionBar(mTbSimple);
        mTbSimple.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

    private void addCallBack() {
        DataBindingUtils.addCallBack(this, mSimpleActivityVM.mUserBeanObservableField, new Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(Observable observable, int i) {
                mUserBean.setUserAddres(mSimpleActivityVM.userAddres.get());
                mUserBean.setUserCard(mSimpleActivityVM.userCard.get());
                mUserBean.setUserEmail(mSimpleActivityVM.userEmail.get());
                mUserBean.setUserName(mSimpleActivityVM.userName.get());
            }
        });
    }


    @Override
    public void setViewModel(BaseVM baseVM) {
        mSimpleBinding = DataBindingUtil.setContentView(this, R.layout.activity_simple);
        mSimpleActivityVM = (SimpleActivityVM) baseVM;
        mSimpleBinding.setSimpleActivityVM(mSimpleActivityVM);
        mUserBean.setUserAddres("shanghai");
        mUserBean.setUserName("Amos");
        mUserBean.setUserEmail("123@163.com");
        mUserBean.setUserCard("819365189");
        mSimpleBinding.setUserBean(mUserBean);
    }
}

這個也是要繼承BaseActivity,也要實現(xiàn)setViewModel這個方法轨蛤,在這里我們做一些數(shù)據(jù)的初始化以及設(shè)置一些Variable蜜宪。

接著是XML文件的代碼:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="simpleActivityVM"
            type="cn.amos.vm.SimpleActivityVM"/>

        <variable
            name="userBean"
            type="cn.amos.model.UserBean"/>
    </data>

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:context="cn.amos.view.SimpleActivity">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar
                android:id="@+id/tb_simple"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:navigationIcon="?attr/homeAsUpIndicator"
                app:popupTheme="@style/AppTheme.PopupOverlay"
                app:title="簡單使用"/>

        </android.support.design.widget.AppBarLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="?attr/actionBarSize"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:text="原來的信息"
                android:textSize="16sp"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="user  Card:"
                    android:textSize="18sp"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@{userBean.userCard}"
                    android:textSize="18sp"/>
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="userName:"
                    android:textSize="18sp"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@{userBean.userName}"
                    android:textSize="18sp"/>
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="userAddre:"
                    android:textSize="18sp"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@{userBean.userAddres}"
                    android:textSize="18sp"/>
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="userEmail:"
                    android:textSize="18sp"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@{userBean.userEmail}"
                    android:textSize="18sp"/>
            </LinearLayout>

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:layout_marginTop="20dp">

                <android.support.design.widget.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="請輸入卡號"
                    android:text="@={simpleActivityVM.userCard}"/>
            </android.support.design.widget.TextInputLayout>

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:layout_marginTop="20dp">

                <android.support.design.widget.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="請輸入用戶名"
                    android:text="@={simpleActivityVM.userName}"/>
            </android.support.design.widget.TextInputLayout>

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:layout_marginTop="20dp">

                <android.support.design.widget.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="請輸入用戶住址"
                    android:text="@={simpleActivityVM.userAddres}"/>
            </android.support.design.widget.TextInputLayout>

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:layout_marginTop="20dp">

                <android.support.design.widget.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="請輸入郵箱"
                    android:text="@={simpleActivityVM.userEmail}"/>
            </android.support.design.widget.TextInputLayout>

            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:onClick="@{simpleActivityVM.updateData}"
                android:text="更新信息"/>
        </LinearLayout>
    </android.support.design.widget.CoordinatorLayout>
</layout>

這里實現(xiàn)的就是,上面是幾個textView祥山,顯示一些最開始的信息圃验,然后后面是幾個EditText和一個Button,在EditText輸入信息缝呕,點擊Button可以更新上面TextView顯示的信息澳窑。這里需要注意的是@={simpleActivityVM.userEmail}斧散,這里,不要忘記那個等號照捡,否則會獲取不到EditText中的信息颅湘。

最后是SimpleActivityVM的代碼:

public class SimpleActivityVM extends BaseVM {
    private static final String TAG = "SimpleActivityVM";
    public ObservableField<UserBean> mUserBeanObservableField = new ObservableField<>();

    public ObservableField<String> userCard = new ObservableField<>("");
    public ObservableField<String> userAddres = new ObservableField<>("");
    public ObservableField<String> userName = new ObservableField<>("");
    public ObservableField<String> userEmail = new ObservableField<>("");

    public SimpleActivityVM() {
    }

    public void updateData(View view) {
        mUserBeanObservableField.notifyChange();
    }
}

這里面的ObservableField<String>是需要更新的屬性话侧,最好實例化栗精。當(dāng)我們點擊Button的時候,會實現(xiàn)回調(diào)瞻鹏,然后在SimpleActivity中重新設(shè)置TextView里面的數(shù)據(jù)悲立,實現(xiàn)更新。

這樣就可以完成我們一開始的需求新博,初始化數(shù)據(jù)薪夕,然后在EditText中輸入,點擊按鈕實現(xiàn)更新赫悄。這里是第一部分簡單的DataBinding的使用原献,下一篇中我們會在RecyclerView、ViewPager等中使用DataBinding埂淮。還有做一些簡單的RecyclerView的封裝姑隅,比如實現(xiàn)下拉刷新和上拉加載更多等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末倔撞,一起剝皮案震驚了整個濱河市讲仰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痪蝇,老刑警劉巖鄙陡,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異躏啰,居然都是意外死亡趁矾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門给僵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毫捣,“玉大人,你說我怎么就攤上這事想际∨嗦” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵胡本,是天一觀的道長牌柄。 經(jīng)常有香客問我,道長侧甫,這世上最難降的妖魔是什么珊佣? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任蹋宦,我火速辦了婚禮,結(jié)果婚禮上咒锻,老公的妹妹穿的比我還像新娘冷冗。我一直安慰自己,他們只是感情好惑艇,可當(dāng)我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布蒿辙。 她就那樣靜靜地躺著,像睡著了一般滨巴。 火紅的嫁衣襯著肌膚如雪思灌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天恭取,我揣著相機與錄音泰偿,去河邊找鬼。 笑死蜈垮,一個胖子當(dāng)著我的面吹牛耗跛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播攒发,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼调塌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了晨继?” 一聲冷哼從身側(cè)響起烟阐,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎紊扬,沒想到半個月后蜒茄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡餐屎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年檀葛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腹缩。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡屿聋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出藏鹊,到底是詐尸還是另有隱情润讥,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布盘寡,位于F島的核電站楚殿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏竿痰。R本人自食惡果不足惜脆粥,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一砌溺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧变隔,春花似錦规伐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至孵户,卻和暖如春萧朝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背夏哭。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留献联,地道東北人竖配。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像里逆,于是被迫代替她去往敵國和親进胯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,562評論 2 349

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