前言
Model:负责数据逻辑
View:负责视图逻辑
ViewModel:负责业务逻辑
持有关系:
1、ViewModel 持有 View
2、ViewModel 持有 Model
3、Model 持有 ViewModel
辅助工具:DataBinding
执行流程:View ==> ViewModel ==> Model ==> ViewModel ==> View
在MVVM中,修改了数据,视图会自动更新相关数据,这个自动通知View更新的功能,由DataBinding完成,所以Model ==> ViewModel ==> View,这个执行流程,并不是通知View刷新数据,而是让View执行其他操作,比如 提交表单后,通知View显示 加载Loading,提交完成后,通知View 隐藏加载Loading。
案例效果图:
1、定义ViewModel接口
/**
* 控制器接口 负责业务逻辑
*/
public interface IViewModel extends IBaseViewModel {
void setView(IView view); // 持有 View
void setModel(IModel model); // 持有 Model
IModel getModel(); // 获取 Model,由View通知 ViewModel
void onDataChanged(String data); // 时时修改Model的数据,由View通知 ViewModel
void submitFromData(); // 执行Model的 提交表单服务,由View通知 ViewModel
void clearData(); // 执行Model的 清空数据方法,由View通知 ViewModel
void showSubmitFromLoading(); // 执行View的显示loading方法,由Model通知 ViewModel
void hideSubmitFromLoading(); // 执行View的隐藏loading方法,由Model通知 ViewModel
}
1.1、实现ViewModel接口
/**
* 业务逻辑 具体实现
*/
public class IViewModelImp implements IViewModel {
private IView view;
private IModel model;
@Override
public void setModel(IModel model) {
this.model = model;
}
@Override
public IModel getModel() {
return model;
}
@Override
public void setView(IView view) {
this.view = view;
}
@Override
public void removeHandlerMsgAndCallback() {
model.removeHandlerMsgAndCallback();
}
@Override
public void onDataChanged(String data) {
model.onDataChanged(data);
}
@Override
public void submitFromData() {
model.submitFromData();
}
@Override
public void clearData() {
model.clearData();
}
@Override
public void showSubmitFromLoading() {
view.showSubmitFromLoading();
}
@Override
public void hideSubmitFromLoading() {
view.hideSubmitFromLoading();
}
}
2、定义Model接口
/**
* 数据模型接口 负责数据逻辑
*/
public interface IModel extends IBaseModel {
void setViewModel(IViewModel viewModel, UserBean userBean); // 持有 ViewModel
/**
* 这些都是方法,都是由 ViewModel 调用的
*/
UserBean getUserBean(); // 提供对外 获取数据的接口
void onDataChanged(String data); // 监听文本变化,时时更新数据,用于单向绑定
void submitFromData(); // 提交表单数据
void clearData(); // 清空数据
}
2.1、实现Model接口
/**
* 数据模型逻辑 具体实现
*/
public class IModelImp implements IModel {
private IViewModel viewModel;
private UserBean user;
private Handler handler = new Handler();
@Override
public void setViewModel(IViewModel viewModel, UserBean userBean) {
this.viewModel = viewModel;
this.user = userBean;
}
@Override
public UserBean getUserBean() {
return user;
}
@Override
public void removeHandlerMsgAndCallback() {
handler.removeCallbacksAndMessages(null);
}
@Override
public void onDataChanged(String data) {
// user.name.setValue(data); // 如果使用 单向绑定,要先更新对象值
}
@Override
public void submitFromData() {
viewModel.showSubmitFromLoading();
handler.removeCallbacksAndMessages(null);
handler.postDelayed(new Runnable() {
@Override
public void run() {
viewModel.hideSubmitFromLoading();
}
}, 1500);
}
@Override
public void clearData() {
user.name.setValue(null);
}
}
3、定义View接口
/**
* 视图接口 负责视图逻辑
*/
public interface IView extends IBaseView {
/**
* 这些都是方法,都是由 ViewModel 调用的
*/
void showSubmitFromLoading(); // 显示提交表单loading
void hideSubmitFromLoading(); // 隐藏提交表单loading
}
3.1、实现View接口
/**
* 视图逻辑 具体实现
*/
public class MVVMActivity extends AppCompatActivity implements IView {
private ActivityMvvmBinding binding;
private AlertDialog dialog;
private IViewModel viewModel;
private IModel model;
private UserBean userBean;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMvvmBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
userBean = new UserBean();
viewModel = new IViewModelImp();
model = new IModelImp();
// 注意一下,写的顺序
viewModel.setView(this); // 持有 View
model.setViewModel(viewModel, userBean); // 持有 ViewModel
viewModel.setModel(model); // 持有 Model
binding.setViewModel(viewModel); // 和xml绑定
binding.setLifecycleOwner(this); // 监听,用于刷新数据的关键
init();
}
private void init() {
binding.edit.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
viewModel.onDataChanged(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
viewModel.removeHandlerMsgAndCallback();
}
@Override
public void showSubmitFromLoading() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
TextView textView = new TextView(this);
String data = userBean.name.getValue();
if (TextUtils.isEmpty(userBean.name.getValue())) {
data = "normal";
}
textView.setText("正在提交:" + data);
builder.setCancelable(false);
builder.setView(textView);
dialog = builder.show();
}
@Override
public void hideSubmitFromLoading() {
dialog.dismiss();
}
@BindingAdapter("isNull")
public static void isNull(TextView view,String name) {
if (TextUtils.isEmpty(name)) {
view.setText("normal");
return;
}
view.setText(name);
}
}
4、IBaseViewModel
/**
* Base 代理接口 负责业务逻辑
*/
public interface IBaseViewModel {
// 写一些,公用或者通用的方法,用于扩展
default void removeHandlerMsgAndCallback() {} // 删除handler 回调和消息
}
5、IBaseModel
/**
* Base 数据模型接口 负责数据逻辑
*/
public interface IBaseModel {
// 写一些,公用或者通用的方法,用于扩展
default void removeHandlerMsgAndCallback() {} // 删除handler 回调和消息
}
6、IBaseView
/**
* Base 视图接口 负责视图逻辑
*/
public interface IBaseView {
// 写一些,公用或者通用的方法,用于扩展
default void testBaseView() {}
}
7、activity_mvvm.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="viewModel"
type="com.example.androidmvvm.mvvm.viewmodel.IViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.activity.MVVMActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="48dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="@color/material_dynamic_primary90"
app:title="MVVM" />
<!-- @=:双向绑定,改变视图上值的同时,对象值也会跟随改变 -->
<EditText
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="@={viewModel.model.userBean.name}"
android:layout_marginHorizontal="16dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar" />
<!-- @:单向绑定,需要先更新对象值,user.name.setValue(data),视图才会刷新 -->
<!-- <EditText-->
<!-- android:id="@+id/edit"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="50dp"-->
<!-- android:text="@{viewModel.model.userBean.name}"-->
<!-- android:layout_marginHorizontal="16dp"-->
<!-- app:layout_constraintLeft_toLeftOf="parent"-->
<!-- app:layout_constraintRight_toRightOf="parent"-->
<!-- app:layout_constraintTop_toBottomOf="@id/toolbar" />-->
<TextView
android:id="@+id/edit_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:isNull="@{viewModel.model.userBean.name}"
app:layout_constraintLeft_toLeftOf="@id/edit"
app:layout_constraintTop_toBottomOf="@id/edit" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/submit_btn"
android:layout_width="match_parent"
android:layout_height="58dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:text="submit"
android:onClick="@{() -> viewModel.submitFromData()}"
android:textAllCaps="false"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_msg" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/clear_btn"
android:layout_width="match_parent"
android:layout_height="58dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:text="clear"
android:onClick="@{() -> viewModel.clearData()}"
android:textAllCaps="false"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/submit_btn" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
8、源码地址
GitHub - LanSeLianMa/AndroidMVVM: Android MVVM 写法