虽然MVVM能够实现给Android的开发带来诸多的方便,但是在某些控件的使用上MVVM架构却比传统的MVP要麻烦许多。
在谷歌官方的mvp样例程序TO-DO中,实现添加一个任务后会在返回TaskFragment时显示一个Snackbar以便提示用户。具体实现效果如下图:
执行过程是:当用户点击右下方红色的添加按钮后,跳转至添加任务的activity进行任务添加,添加完成用户点击返回后,若添加成功,在界面下方显示TO—Do added的添加成功信息。
那么接下来:我们在两种谷歌给出的样例程序中是如何实现他们的吧。
MVP实现过程:
调用Snackbar进行显示的函数为showMessage,实现代码如下:
Private void showMessage(String message){
Snackbar.make(getView(),message,Snackbar.LENGTH_LONG).show();
}
当添加成功后返回时,在Presenter层中利用持有的view层引用(此处持有的是TaskFragment的引用),回调的是view层中的showSuccessfullySavedMessage()方法,而在view层的实现代码中,该方法调用了showMessage方法进行Snackbar的显示,从而达到在界面提醒用户的效果。具体代码如下:
@Override
Public void result(int requestCode,int resultCode){
//If a taskw as successfully added,show show Successfully Saved Message
if(AddEditTaskActivity.REQUEST_ADD_TASK==requestCode&&Activity.RESULT_OK==resultCode){
mTasksView.showSuccessfullySavedMessage();
}
}
注:result是界面跳转之间的回调函数,当成功添加新任务后,presenter层会调用此方法。
view层中实现的showSuccessfullySavedMessage()方法代码如下:
@Override
public void showSuccessfullySavedMessage() {
showMessage(getString(R.string.successfully_saved_task_message));
}
可见一个snackbar的显示在MVP结构中是并不复杂的。下面在看看mvvm中是怎么实现的。
MVVM实现过程:
首先如Toast一样,snackbar是不需要在xml中进行编写的,因此mvvm的双向绑定机制对于snackbar是不适用的,让我们看看谷歌官方给出的代码是怎么解决这一问题的吧。
首先为了提高代码的复用性与方便使用,谷歌开发人员编写了一个SnackbarUtil类,代码如下:
/**
* Provides a method to show a Snackbar.
*/
public class SnackbarUtils {
public static void showSnackbar(View v, String snackbarText) {//传入Context和要snackbar需要显示的内容即可显示
if (v == null || snackbarText == null) {
return;
}
Snackbar.make(v, snackbarText, Snackbar.LENGTH_LONG).show();//负责构建Snackbar进行前台的显示
}
}
view层依然为TaskFragment类)。
首先我们看到在view层中定义了一个字段为:
Private Observable.OnPropertyChangedCallback mSnackbarCallback;
然后我们依据Fragment的生命周期继续进行追踪:发现在OnActivityCreat方法(此方法在加载fragment时在OnCreatView后执行)中调用了setUpSnackbar()方法,那么看看setUpSnackbar方法干了些什么吧,代码如下:
private void setupSnackbar() {
mSnackbarCallback = new Observable.OnPropertyChangedCallback() {//字段值更改回调函数
@Override
public void onPropertyChanged(Observable observable, int i) {
SnackbarUtils.showSnackbar(getView(), mTasksViewModel.getSnackbarText());
}
};
mTasksViewModel.snackbarText.addOnPropertyChangedCallback(mSnackbarCallback);//将监听进行传递
}
其中mTasksViewModel是该TaskView的ViewModel层,这里用到了viewModel层的snackbarText字段,利用该字段的addOnPropertyChangedCallback方法向其添加了一个mSnackbarCallback对象,而mSnackbarCallback对象的实例化也在上面的函数中完成,在其复写方法中调用了SnackbarUtil类的showSnackbar显示方法(指明了context和显示内容),在界面进行数据显示。那么至此我们可以跳至viewModel层看看viewModel层是怎么进行处理的。
首先看到viewModel层中定义了一个字段:
Final ObservableField snackbarText = new ObservableField<>();//定义一个可观察的字段,泛型数据类型为String
其中ObservableField是一个可观测字段,继承了BaseObservable,定义为此类型的字段含有get,set等方法,在set方法中调用了BaseObservable的notifyChange方法,这时便可以利用view层传递过来的回调进行显示的更新了。
让我们再往下看,当添加任务成功后调用的是如下的函数:
void handleActivityResult(int requestCode, int resultCode) {
if (AddEditTaskActivity.REQUEST_CODE == requestCode) {
switch (resultCode) {
case TaskDetailActivity.EDIT_RESULT_OK:
snackbarText.set(
mContext.getString(R.string.successfully_saved_task_message));
break;
case AddEditTaskActivity.ADD_EDIT_RESULT_OK:
snackbarText.set(
mContext.getString(R.string.successfully_added_task_message));
break;
case TaskDetailActivity.DELETE_RESULT_OK:
snackbarText.set(
mContext.getString(R.string.successfully_deleted_task_message));
break;
}
}
其中
Case TaskDetailActivity.EDIT_RESULT_OK:snackbarText.set(mContext.getString
(R.string.successfully_saved_task_message));
break;
是对添加成功任务后的情况进行处理,当任务添加成功回调后根据返回任务添加成功的结果便可以向用户进行提醒了。等等,这里没有回调至view层,是怎么进行显示的呢?
前面分析过,在view层向viewModel层的snackbarText字段添加了一个OnPropertyChangedCallback,在其进行新建的时候便指明了context与需要显示的数据来源,当snackbarText字段的set函数执行后,其内部的notifyChange方法也会得到执行,此时便会执行到view层的setSnackbar中的回调函数复写方法,此时界面便呈现需要向用户提示的信息了。
为了进一步追踪这个接口,我们继续向下看,发现在view层的OnDetroy方法中,对回调接口进行了删除,具体代码如下:
@Override
public void onDestroy() {
mListAdapter.onDestroy();
if (mSnackbarCallback != null) {
mTasksViewModel.snackbarText.removeOnPropertyChangedCallback(mSnackbarCallback);//删除回调
}
super.onDestroy();
}
至此我们将MVVM模式中的snackbar从开始至结束讲解完毕了。
可以看到在MVVM模式中实现该控件的方式确实与MVP模式有不小的差异。
以上纯粹是个人的愚见,有不妥之处希望各位不吝赐教!