前面一段時間學(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)下拉刷新和上拉加載更多等。