Android MVVM 写法

前言

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接口

java 复制代码
/**
 * 控制器接口 负责业务逻辑
 */
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接口

java 复制代码
/**
 * 业务逻辑 具体实现
 */
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接口

java 复制代码
/**
 * 数据模型接口 负责数据逻辑
 */
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接口

java 复制代码
/**
 * 数据模型逻辑 具体实现
 */
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接口

java 复制代码
/**
 * 视图接口 负责视图逻辑
 */
public interface IView extends IBaseView {

    /**
     * 这些都是方法,都是由 ViewModel 调用的
     */

    void showSubmitFromLoading(); // 显示提交表单loading

    void hideSubmitFromLoading(); // 隐藏提交表单loading

}

3.1、实现View接口

java 复制代码
/**
 * 视图逻辑 具体实现
 */
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

java 复制代码
/**
 * Base 代理接口 负责业务逻辑
 */
public interface IBaseViewModel {

    // 写一些,公用或者通用的方法,用于扩展

    default void removeHandlerMsgAndCallback() {} // 删除handler 回调和消息

}

5、IBaseModel

java 复制代码
/**
 * Base 数据模型接口 负责数据逻辑
 */
public interface IBaseModel {

    // 写一些,公用或者通用的方法,用于扩展

    default void removeHandlerMsgAndCallback() {} // 删除handler 回调和消息

}

6、IBaseView

java 复制代码
/**
 * Base 视图接口 负责视图逻辑
 */
public interface IBaseView {

    // 写一些,公用或者通用的方法,用于扩展

    default void testBaseView() {}

}

7、activity_mvvm.xml

java 复制代码
<?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 写法

9、其他写法

Android MVC 写法-CSDN博客

Android MVP 写法-CSDN博客

相关推荐
雨白2 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹3 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空5 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭5 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日6 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安6 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑6 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟11 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡12 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0012 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体