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

/**
 * 控制器接口 负责业务逻辑
 */
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 写法

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenStack云计算(-) 简介与部署Keystone

一.OpenStack简介 什么是云计算:云计算是一种按使用量付费的模式,这种模式提供可用的、便捷的、按需的网络访问,进入可配置的计算资源共享池(资源包括网络,服务器,存储,应用软件,服务) 云计算所包含的几个层次服务&#xff1a; SaaS ( Software as a Service ) :把在线软件作…

【开源】基于Vue+SpringBoot的公司货物订单管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 客户管理模块2.2 商品维护模块2.3 供应商管理模块2.4 订单管理模块 三、系统展示四、核心代码4.1 查询供应商信息4.2 新增商品信息4.3 查询客户信息4.4 新增订单信息4.5 添加跟进子订单 五、免责说明 一、摘要 1.1 项目…

Prometheus通过consul实现自动服务发现

环境,软件准备 本次演示环境&#xff0c;我是在虚拟机上安装 Linux 系统来执行操作&#xff0c;以下是安装的软件及版本&#xff1a; System: CentOS Linux release 7.6Docker: 24.0.5Prometheus: v2.37.6Consul: 1.6.1 注意&#xff1a;这里为了方便启动 Prometheus、Consul服…

Elasticsearch:使用 ELSER v2 文本扩展进行语义搜索

Elastic 提供了一个强大的 ELSER 供我们进行语义搜索。ELSER 是一种稀疏向量的搜索方法。我们无需对它做任何的微调及训练。它是一种 out-of-domain 的模型。目前它仅对英文进行支持。希望将来它能对其它的语言支持的更好。更多关于 ELSER 的知识&#xff0c;请参阅文章 “Elas…

YOLOv8改进 | 细节创新篇 | iAFF迭代注意力特征融合助力多目标细节涨点

一、本文介绍 本文给大家带来的改进机制是iAFF&#xff08;迭代注意力特征融合&#xff09;&#xff0c;其主要思想是通过改善特征融合过程来提高检测精度。传统的特征融合方法如加法或串联简单&#xff0c;未考虑到特定对象的融合适用性。iAFF通过引入多尺度通道注意力模块(我…

winserver2008 r2服务器iis配置支持flv,f4v,mp4格式视频

很多政府单位网站一直在使用WIN服务器&#xff0c;大部分网站都使用多年基本使用.NET或者CMS系统建站&#xff0c;系统环境也一直是老版本&#xff0c;今天在维护过程中又出现了新问题&#xff0c;上传的MP4文件不支持网站上播放&#xff0c;顺便也分享下解决过程。当我们架设的…

数字身份验证:跨境电商如何应对账户安全挑战?

在数字化时代&#xff0c;随着跨境电商的蓬勃发展&#xff0c;账户安全问题逐渐成为行业和消费者关注的焦点。随着网络犯罪日益猖獗&#xff0c;用户的数字身份安全面临着更加复杂的威胁。本文将深入探讨数字身份验证在跨境电商中的重要性&#xff0c;并探讨各种创新技术和策略…

回归预测 | MATLAB实ZOA-LSTM基于斑马优化算法优化长短期记忆神经网络的多输入单输出数据回归预测模型 (多指标,多图)

回归预测 | MATLAB实ZOA-LSTM基于斑马优化算法优化长短期记忆神经网络的多输入单输出数据回归预测模型 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实ZOA-LSTM基于斑马优化算法优化长短期记忆神经网络的多输入单输出数据回归预测模型 &#xff08;…

C++系列-第1章顺序结构-4-整型int

C系列-第1章顺序结构-4-整型int 在线练习&#xff1a; http://noi.openjudge.cn/ https://www.luogu.com.cn/ 总结 本文是C系列博客&#xff0c;主要讲述整型int的用法 整型int 在C中&#xff0c;int 是一个关键字&#xff0c;用于声明整型变量。int 类型用于存储整数&…

PHP序列化总结2--常见的魔术方法

魔术方法的概念 PHP的魔术方法是一种特殊的方法&#xff0c;用于覆盖PHP的默认操作。它们以双下划线&#xff08;__&#xff09;开头&#xff0c;后面跟着一些特定的字符串&#xff0c;如__construct()、__destruct()、__get()等。这些魔术方法在对象执行特定操作时被自动调用…

三台CentOS7.6虚拟机搭建Hadoop完全分布式集群(三)

这个是笔者大学时期的大数据课程使用三台CentOS7.6虚拟机搭建完全分布式集群的案例&#xff0c;已成功搭建完全分布式集群&#xff0c;并测试跑实例。 9 安装hbase 温馨提示&#xff1a;安装hbase先在master主节点上配置&#xff0c;然后远程复制到slave01或slave02 &#xf…

win上使用wireshark 抓包 | 安装、实战抓包、筛选规则

先随便讲两句吧 win 上抓包&#xff0c;使用wireshark 直接运行&#xff0c;通过选定网卡、配置筛选规则 相比&#xff0c;在linux 上抓包&#xff0c;直接使用命令 tcpdump 再添加筛选规则 就可以 好像wireshark的一个插件不维护&#xff0c;导致需要重新安装插件&#xff0c;…

PBR纹理贴图类型详解

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 PBR 纹理是一种帮助 3D 艺术家使他们的 3D 渲染看起来更逼真的技术。…

鸿鹄电子招投标系统:基于Spring Boot、Mybatis、Redis和Layui的企业电子招采平台源码与立项流程

在数字化时代&#xff0c;企业需要借助先进的数字化技术来提高工程管理效率和质量。招投标管理系统作为企业内部业务项目管理的重要应用平台&#xff0c;涵盖了门户管理、立项管理、采购项目管理、采购公告管理、考核管理、报表管理、评审管理、企业管理、采购管理和系统管理等…

学习笔记:R语言基础

文章目录 一、R语言简介二、选择R的原因三、R基本数据对象&#xff08;一&#xff09;向量&#xff08;二&#xff09;矩阵&#xff08;三&#xff09;数组&#xff08;四&#xff09;因子&#xff08;五&#xff09;列表&#xff08;六&#xff09;数据框&#xff08;七&#…

Android笔记(二十三):Paging3分页加载库结合Compose的实现分层数据源访问

在Android笔记&#xff08;二十二&#xff09;&#xff1a;Paging3分页加载库结合Compose的实现网络单一数据源访问一文中&#xff0c;实现了单一数据源的访问。在实际运行中&#xff0c;往往希望不是单纯地访问网络数据&#xff0c;更希望将访问的网络数据保存到移动终端的SQL…

分布式系统架构设计之分布式系统实践案例和未来展望

分布式系统在过去的几十年里经历了长足的发展&#xff0c;从最初的简单分布式架构到今天的微服务、云原生等先进架构&#xff0c;取得了丰硕的成果。本文将通过实际案例分享分布式系统的架构实践&#xff0c;并展望未来可能的发展方向。 一、实践案例 1、微服务化实践 背景 …

【neo4j】desktop下载

【neo4j】desktop下载 https://neo4j.com/download/ 点击download&#xff0c;填写表格 之后就可以正常使用了

Python+Yolov5+Qt交通标志特征识别窗体界面相片视频摄像头

程序示例精选 PythonYolov5Qt交通标志特征识别窗体界面相片视频摄像头 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonYolov5Qt交通标志特征识别窗体界面相片视频摄像头》编写代码&a…

4.16 构建onnx结构模型-And

前言 构建onnx方式通常有两种&#xff1a; 1、通过代码转换成onnx结构&#xff0c;比如pytorch —> onnx 2、通过onnx 自定义结点&#xff0c;图&#xff0c;生成onnx结构 本文主要是简单学习和使用两种不同onnx结构&#xff0c; 下面以 And 结点进行分析 方式 方法一&…