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博客

相关推荐
三少爷的鞋5 小时前
AI 时代,我们都将成为通才型开发者:只懂 Android,已经不够了
android
Dovis(誓平步青云)9 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
晚风叙码11 小时前
MySQL 数据类型详解:从数值到字符串,一篇讲透
android·mysql·adb
传奇开心果编程11 小时前
【Compose Multiplatform 跨端开发学与练】第3课 布局与组件
android·windows·学习·ui·ios·kotlin·composer
samble12 小时前
从零搭建灌装监控系统(十三):报警系统设计,生命周期与通知
c#·wpf·mvvm·modbus·工业控制
传奇开心果编程14 小时前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程15 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程16 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程16 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓16 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android