Android使用DataBinding实现双向绑定(一)

前面一段时间学习了一下Android中的DataBinding,但是只是很简单地实现了一下,DataBinding中最强大的地方还没有认真地学习过,有很多地方还不理解。这次,深入学习一下DataBinding的双向绑定和MVVM模式。

1、实现简单的使用

先回顾一下简单的使用,使用的时候需要在模块的build.gradle文件中添加这一句:

dataBinding{
    enabled=true
}

添加这一句之后,gladle文件sync之后就可以开始使用。

接着我们开始搭建一个基于MVVM+DataBinding简单的开发框架。

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在具体的项目中会封装一些常用的方法,比如实现的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层的话很多都是数据bean,这里我们新建一个简单的bean,这个bean只是简单的封装一些显式信息,为了可以实现自动更新,我们需要做一些处理,具体的代码如下:

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注解为了实现自动更新,也是需要在set方法中添加notifyPropertyChanged(cn.amos.BR.userEmail);这一句代码,这里是提醒更新数据。需要注意的是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));
            }
        }
    }
}

这里封装了两个方法,第一个是添加回调,另外一个就是移除回调,没有什么很复杂的东西,需要解析一下的就是:Observable.OnPropertyChangedCallback,这是一个当observable中的属性发生改变时由Observable调用的回调接口,需要实现的就是一个onPropertyChanged(Observable observable, int i)这个方法,两个属性,第一个是observable是正在改变的observable,另外一个是需要用BR标识,并且get方法上面需要添加@Bindable注解。需要了解这个API的,可以点击这里

到这里,基本就可以完成了框架的简易搭建,当然,这里只是非常简单的,在实际项目中还需要其他的东西,这里只是一个简单的Demo,所以就简单一点,接下来我们开始实现具体的业务。

2、实现按钮的点击

首先我们先实现一个简单的按钮点击,跳转另外界面的一个需求。

首先是新建一个activity和对应的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,然后需要实现setViewModel(BaseVM baseVM)这个方法,这个是在IView这个接口中的,每个继承于BaseActivity的都需要实现这个方法,这个方法就是设置一个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="简单实现绑定数据"/>
    </RelativeLayout>
</layout>

这里需要一个MainActivityVM,这个类的代码是:

public class MainActivityVM extends BaseVM {

    public ObservableInt goToSimple = new ObservableInt();

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

这里就一个方法,这个方法GoToNext,这个是button的点击事件,注意需要传入一个View的参数。这里调用了 goToSimple.notifyChange();这个方法,会在MainActivity中实现回调,我们在MainActivity中有一个addCallBack的方法中添加了这个回调的监听。

public ObservableInt goToSimple = new ObservableInt();

这个也是实现数据更新的,不过比实现BaseObservable这个类更加细。

这样就可以实现点击跳转了。

3、实现一些简单信息更新

新建一个SimpleActivity、对应的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,也要实现setViewModel这个方法,在这里我们做一些数据的初始化以及设置一些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>

这里实现的就是,上面是几个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>是需要更新的属性,最好实例化。当我们点击Button的时候,会实现回调,然后在SimpleActivity中重新设置TextView里面的数据,实现更新。

这样就可以完成我们一开始的需求,初始化数据,然后在EditText中输入,点击按钮实现更新。这里是第一部分简单的DataBinding的使用,下一篇中我们会在RecyclerView、ViewPager等中使用DataBinding。还有做一些简单的RecyclerView的封装,比如实现下拉刷新和上拉加载更多等。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容