Android MVVM 写法

news2024/12/23 19:16:33

 前言

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 写法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1343834.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux源码编译升级安装openssl3.0.1导致系统启动失败的问题解决

前两天在安装curl的时候&#xff0c;提示openssl版本太老了&#xff0c;原有的版本是openssl1.0的版本&#xff0c;需要将其升级到openssl3的版本。 直接使用命令行sudo apt install默认安装的还是openssl1.1.1版本&#xff0c;因此决定使用源码自行安装。 具体的安装过程就不赘…

webpack打包批量替换路径(string-replace-webpack-plugin插件)

string-replace-webpack-plugin 是一个用于在 webpack 打包后的文件中替换字符串的插件。它可以用于将特定字符串替换为其他字符串&#xff0c;例如将敏感信息从源代码中移除或对特定文本进行本地化处理。比如文件的html、css、js中的路径地址想批量更改一下 http://localhost:…

海德堡UV灯电源维修eta Plus Elc PE22-400-210

uv灯电源维修故障包括&#xff1a; 1、电压不稳&#xff1a;检查uv打印机的电压&#xff0c;设置一个稳压箱即可。 2、温度过高&#xff1a;uv打印机温度过高也会影响uv灯&#xff0c;可以更换为水冷式循环降温。 3、水箱里的信号线接触不好&#xff1a;将两边的信号线对调&…

leetcode刷题记录07(2023-04-30)【二叉树展开为链表 | 买卖股票的最佳时机 | 二叉树中的最大路径和(递归) | 最长连续序列(并查集)】

114. 二叉树展开为链表 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺…

ArcGIS批量计算shp面积并导出shp数据总面积(建模法)

在处理shp数据时&#xff0c; 又是我们需要知道许多个shp字段的批量计算&#xff0c;例如计算shp的总面积、面积平均值等&#xff0c;但是单个查看shp文件的属性进行汇总过于繁琐&#xff0c;因此可以借助建模批处理来计算。 首先准备数据&#xff1a;一个含有多个shp的文件夹。…

前后端分离nodejs+vue+ElementUi网上订餐系统69b9

课题主要分为两大模块&#xff1a;即管理员模块和用户模块&#xff0c;主要功能包括个人中心、用户管理、菜品类型管理、菜品信息管理、留言反馈、在线交流、系统管理、订单管理等&#xff1b; 运行软件:vscode 前端nodejsvueElementUi 语言 node.js 框架&#xff1a;Express/k…

Lumerical------按键中断程序执行

Lumerical------中断程序执行 引言正文 引言 在 Lumerical 中&#xff0c;很多时候我们需要通过 sweep 的方式来获取我们想要的结果&#xff0c;然而&#xff0c;有时候当我们运行程序后发现书写的脚本有问题时&#xff0c;我们想要强行终止程序的执行&#xff0c;该怎么办呢&…

代码随想录-刷题第四十二天

0-1背包理论基础 0-1背包问题介绍 0-1背包问题&#xff1a;有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 0-1背包问题可以使用回溯法进…

Collector收集器的高级用法

Collectors收集器的高级用法 场景1&#xff1a;获取关联的班级名称 原先如果需要通过关联字段拿到其他表的某个字段&#xff0c;只能遍历List匹配获取 for (Student student : studentList) {Long clazzId student.getClazzId();// 遍历班级列表&#xff0c;获取学生对应班级…

【ArkTS入门】ArkTS开发初探:语言特点和开发特点

什么是ArkTS&#xff1f; ArkTS是一个为鸿蒙组件而生的框架&#xff0c;语法亲人好用。基于TypeScript&#xff0c;ArkTS拓展了声明式UI、状态管理等的能力&#xff0c;从本质上来讲&#xff0c;是TypeScript的扩展&#xff0c;主要服务于前端。 ArkTS的开发可以满足“一次开…

vue-cli3/webpack打包时去掉console.log调试信息

文章目录 前言一、terser-webpack-plugin是什么&#xff1f;二、使用配置vue-cli项目 前言 开发环境下&#xff0c;console.log调试信息&#xff0c;有助于我们找到错误&#xff0c;但在生产环境&#xff0c;不需要console.log打印调试信息&#xff0c;所以打包时需要将consol…

【React源码 - ReactDom.render发生了什么】

在React开发中&#xff0c;在入口文件我们都会执行ReactDom.render来讲整个应用挂载在主document中&#xff0c;那其中发生了什么&#xff0c;React是如何讲我们写的JSX代码&#xff0c;一步一步更新Fiber进而挂载渲染的呢。本文主要是基于react17.0.2的源码以及自己的理解来简…

thinkphp+vue+mysql企业车辆管理系统m117l

“企业车辆管理系统”是运用php语言和vue框架&#xff0c;以Mysql数据库为基础而发出来的。为保证我国经济的持续性发展&#xff0c;必须要让互联网信息时代在我国日益壮大&#xff0c;蓬勃发展。伴随着信息社会的飞速发展&#xff0c;企业车辆管理系统所面临的问题也一个接一个…

大数据背后的绿色收割:基于Hadoop的农产品价格信息智能分析

大数据背后的绿色收割&#xff1a;基于Hadoop的农产品价格信息智能分析 引言正文1. 数据获取与准备2. 数据清洗与处理3. Hadoop数据分析引擎的运用4. MySQL数据库的集成5. 创新性的可视化6. 结论与展望 结语 引言 随着信息技术的不断发展&#xff0c;农业领域也在数字化的浪潮…

C++ Primer Plus----第十二章--类和动态内存分布

本章内容包括&#xff1a;对类成员使用动态内存分配&#xff1b;隐式和显式复制构造函数&#xff1b;隐式和显式重载赋值运算符&#xff1b;在构造函数中使用new所必须完成的工作&#xff1b;使用静态类成员&#xff1b;将定位new运算符用于对象&#xff1b;使用指向对象的指针…

鸿蒙(HarmonyOS 3.1) DevEco Studio 3.1开发环境汉化

鸿蒙&#xff08;HarmonyOS 3.1&#xff09; DevEco Studio 3.1开发环境汉化 一、安装环境 操作系统: Windows 10 专业版 IDE:DevEco Studio 3.1 SDK:HarmonyOS 3.1 二、设置过程 打开IDE&#xff0c;在第一个菜单File 中找到Settings...菜单 在Setting...中找到Plugins…

毅速:一文说清3D打印随形水路的优势

3D打印发展如火如荼&#xff0c;对于模具行业来说&#xff0c;3D打印对模具水路的改变将产生哪些影响&#xff1f; 优化水路设计。通过3D打印技术&#xff0c;可以快速制造出复杂的内部结构和任意几何形状的模具&#xff0c;从而摆脱传统方法对水路加工的诸多限制。设计师可以…

准备用vscode代替sourceinsight

vscode版本1.85.1 有的符号&#xff0c;sourceinsight解析不到。 看网上说vscode内置了ripgrep&#xff0c;但ctrlshiftf在文件里查找的时候&#xff0c;速度特别慢&#xff0c;根本不像ripgrep的速度。ripgrep的速度是很快的。 但今天再查询&#xff0c;速度又很快了&#x…

mac node基本操作

1 查看所有版本 npm view node versions输出 2 查看已经安装的版本 n list3 安装指定版本 sudo -E n 16.0.04 切换版本 sudo n 16.0.05 查看版本 node -v

[ 云计算 | AWS ] 对比分析:Amazon SNS 与 SQS 消息服务的异同与选择

文章目录 一、前言二、Amazon SNS 服务&#xff08;Amazon Simple Notification Service&#xff09;三、Amazon SQS 服务&#xff08;Amazon Simple Queue Service&#xff09;四、SNS 与 SQS 的区别&#xff08;本文重点&#xff09;4.1 基于推送和轮询区别4.2 消费者数量对应…