MVP中随着业务逻辑的增加,UI的改变多的情况下,会有非常多的跟UI相关的情况,这样就会造成View的接口会很庞大。而MVVM就解决了这个问题,通过双向绑定的机制,实现数据和UI内容,只要想改其中一方,另一方都能够及时更新的一种设计理念,这样就省去了很多在View层中写很多case的情况,只需要改变数据就行。
MVVM 由下面三层组成:
MVVM 是一种思想,一种架构模式,而 DataBinding 是谷歌推出的方便实现 MVVM 的工具。在 DataBinding 库之前,我们经常会写一些重复性很高而且毫无营养的代码,比如:findViewById()、setText()、setOnClickListener() 等。直到2015谷歌 I/O大会推出了 DataBinding,一个实现视图和数据双向绑定的工具。使用 DataBinding 库以后,可以使用声明式布局文件来减少粘结业务逻辑和布局文件的胶水代码,有利于开发者更方便地实现 MVVM 模式。
DataBinfing的使用详细见下面文章:
Android DataBinding使用详解
LiveData和ViewModel是Android Architecture Components中推出的类。使用详细见下面文章:
Android Architecture Components
下面使用MVVM实现一个天气查询的例子。天气查询的api使用聚合数据提供的api。
在build.gradle的android下添加如下代码:
dataBinding{
enabled true
}
public class WeatherInfo {
private String reason;
private ResultBean result;
private int error_code;
public static class ResultBean {
private String city;
private RealtimeBean realtime;
private List future;
public static class RealtimeBean {
private String temperature;
private String humidity;
private String info;
private String wid;
private String direct;
private String power;
private String aqi;
}
public static class FutureBean {
private String date;
private String temperature;
private String weather;
private WidBean wid;
private String direct;
public static class WidBean {
private String day;
private String night;
}
}
}
}
为了节约篇幅我省略了get set方法。
天气Model用于联机获取天气信息,这里使用Retrofit和RxJava进行Http请求,具体实现这里不介绍。由于获取天气联机是异步操作,我们需要定义接口回调请求结果。接口定义如下:
public interface GetRemoteListener {
void onSuccess(T t);
void onError(String msg);
}
WeatherModel的定义如下:
public class WeatherModel {
public void getWeatherInfo(String city, final GetRemoteListener listener) {
HttpHelper.getWeatherInfoApi(city).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(WeatherInfo value) {
listener.onSuccess(value);
}
@Override
public void onError(Throwable e) {
listener.onError(e.getMessage());
}
@Override
public void onComplete() {
}
});
}
}
定义需要和用到的对象。这些对象都是用于界面展示的。
/**
* 后台返回查询天气实体对象
*/
private MutableLiveData weatherInfo = new MutableLiveData<>();
/**
* 用户数据查询天气的城市
*/
private MutableLiveData inputCity = new MutableLiveData<>();
/**
* 界面提示的错误信息
*/
private MutableLiveData errMsg = new MutableLiveData<>();
/**
* 是否正在联机标志。其值改变时通过代码控制显示和隐藏Dialog
*/
private MutableLiveData isLoading = new MutableLiveData<>();
并且要为上面这些变量设置Getter方法,否则在布局文件中不能获取上面的变量。
定义获取天气信息的方法:
public void getWeatherWithNet(){
beforeOnlineHandle();
weatherModel.getWeatherInfo(inputCity.getValue(), new GetRemoteListener() {
@Override
public void onSuccess(WeatherInfo weatherInfo) {
if(weatherInfo.getError_code() == 0) {
WeatherViewModel.this.weatherInfo.setValue(weatherInfo);
}else{
errMsg.setValue(weatherInfo.getReason());
}
isLoading.setValue(false);
}
@Override
public void onError(String msg) {
errMsg.setValue(msg);
isLoading.setValue(false);
}
});
}
public void beforeOnlineHandle(){
isLoading.setValue(true);
errMsg.setValue(null);
}
这里就是定义Activity和其布局文件。注意android中的View包含布局文件中的和代码生成的View两部分。
布局文件最外层是layout,其中包含data和布局文件。data定义数据,布局文件定义布局。
上面需要注意的点:
代码如下,具体说明见注释。
public class WeatherActivity extends AppCompatActivity implements View.OnClickListener {
private WeatherViewModel viewModel;
private ActivityWeatherBinding dataBinding;
private MaterialDialog loadingDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initViewModel();
initDataBinding();
setListener();
}
// 说明1
private void initViewModel() {
// 获取ViewModel,屏幕旋转不是新建,只是重连
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())
).get(WeatherViewModel.class);
// 使用LiveData的功能,当isLoading值改变时回调这里代码控制界面的显示
viewModel.getIsLoading().observe(this, new Observer() {
@Override
public void onChanged(Boolean aBoolean) {
if (aBoolean) {
showLoadingDialog();
} else {
dismissLoadingDialog();
}
}
});
}
// 说明2
private void initDataBinding() {
dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_weather);
dataBinding.setViewModel(viewModel);
// 必须设置
dataBinding.setLifecycleOwner(this);
}
private void setListener() {
findViewById(R.id.btn_request).setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_request:
viewModel.getWeatherWithNet();
break;
}
}
/**
* 代码显示Dialog
*/
private void showLoadingDialog() {
if (loadingDialog == null) {
loadingDialog = new MaterialDialog.Builder(this)
.content("拼命加载中...")
.progress(true, 0)
.build();
}
loadingDialog.show();
}
/**
* 代码隐藏Dialog
*/
private void dismissLoadingDialog() {
loadingDialog.dismiss();
}
}
1、xml定义的控件控制是否显示在ViewModel中定义一个bool类型变量控制
2、代码定义控件控制是否显示在代码中使用viewModel.getIsLoading().observe(this, new Observer