前面一段时间学习了一下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是需要更新的属性,最好实例化。当我们点击Button的时候,会实现回调,然后在SimpleActivity中重新设置TextView里面的数据,实现更新。
这样就可以完成我们一开始的需求,初始化数据,然后在EditText中输入,点击按钮实现更新。这里是第一部分简单的DataBinding的使用,下一篇中我们会在RecyclerView、ViewPager等中使用DataBinding。还有做一些简单的RecyclerView的封装,比如实现下拉刷新和上拉加载更多等。