Android MVVM 写法

news/2024/6/27 20:18:24

 前言

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); // 持有 Viewvoid setModel(IModel model);  // 持有 ModelIModel getModel(); // 获取 Model,由View通知 ViewModelvoid onDataChanged(String data); // 时时修改Model的数据,由View通知 ViewModelvoid submitFromData(); // 执行Model的 提交表单服务,由View通知 ViewModelvoid clearData(); // 执行Model的 清空数据方法,由View通知 ViewModelvoid showSubmitFromLoading(); // 执行View的显示loading方法,由Model通知 ViewModelvoid hideSubmitFromLoading(); // 执行View的隐藏loading方法,由Model通知 ViewModel}

1.1、实现ViewModel接口

/*** 业务逻辑 具体实现*/
public class IViewModelImp implements IViewModel {private IView view;private IModel model;@Overridepublic void setModel(IModel model) {this.model = model;}@Overridepublic IModel getModel() {return model;}@Overridepublic void setView(IView view) {this.view = view;}@Overridepublic void removeHandlerMsgAndCallback() {model.removeHandlerMsgAndCallback();}@Overridepublic void onDataChanged(String data) {model.onDataChanged(data);}@Overridepublic void submitFromData() {model.submitFromData();}@Overridepublic void clearData() {model.clearData();}@Overridepublic void showSubmitFromLoading() {view.showSubmitFromLoading();}@Overridepublic 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();@Overridepublic void setViewModel(IViewModel viewModel, UserBean userBean) {this.viewModel = viewModel;this.user = userBean;}@Overridepublic UserBean getUserBean() {return user;}@Overridepublic void removeHandlerMsgAndCallback() {handler.removeCallbacksAndMessages(null);}@Overridepublic void onDataChanged(String data) {// user.name.setValue(data); // 如果使用 单向绑定,要先更新对象值}@Overridepublic void submitFromData() {viewModel.showSubmitFromLoading();handler.removeCallbacksAndMessages(null);handler.postDelayed(new Runnable() {@Overridepublic void run() {viewModel.hideSubmitFromLoading();}}, 1500);}@Overridepublic void clearData() {user.name.setValue(null);}}

3、定义View接口

/*** 视图接口 负责视图逻辑*/
public interface IView extends IBaseView {/*** 这些都是方法,都是由 ViewModel 调用的*/void showSubmitFromLoading(); // 显示提交表单loadingvoid 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;@Overrideprotected 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); // 持有 Viewmodel.setViewModel(viewModel, userBean); // 持有 ViewModelviewModel.setModel(model); // 持有 Modelbinding.setViewModel(viewModel); // 和xml绑定binding.setLifecycleOwner(this); // 监听,用于刷新数据的关键init();}private void init() {binding.edit.addTextChangedListener(new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {viewModel.onDataChanged(s.toString());}@Overridepublic void afterTextChanged(Editable s) {}});}@Overrideprotected void onDestroy() {super.onDestroy();viewModel.removeHandlerMsgAndCallback();}@Overridepublic 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();}@Overridepublic 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><variablename="viewModel"type="com.example.androidmvvm.mvvm.viewmodel.IViewModel" /></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".ui.activity.MVVMActivity"><androidx.appcompat.widget.Toolbarandroid: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" /><!--   @=:双向绑定,改变视图上值的同时,对象值也会跟随改变   --><EditTextandroid: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" />--><TextViewandroid: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.AppCompatButtonandroid: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.AppCompatButtonandroid: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博客

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

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

相关文章

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的固定帧率(C#)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的固定帧率&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机的固定帧率功能的技术背景CameraExplorer如何查看相机固定帧率功能在NEOAPI SDK里通过函数设置相机固定帧率 Baumer工业相机通过NEOAPI SDK设置相机固…

MySQL检索距离当前最近的7个小时内,靠近每个时间点数据信息

MySQL检索距离当前最近的7个小时内&#xff0c;靠近每个时间点数据信息 如果你想在最近7个小时内找到每个时间点最接近的数据&#xff0c;即使某些时间点没有数据&#xff0c;你可以使用子查询和窗口函数。以下是一个示例查询&#xff1a; sqlCopy codeSELECTt.time_point,CO…

十六:爬虫-验证码与字体反爬

一&#xff1a;验证码 验证码&#xff08;CAPTCHA&#xff09;是一种计算机程序设计技术&#xff0c;用于确定用户是人类而不是机器。它通常表现为一种图像或声音&#xff0c;要求用户在输入框中输入正确的文本或数字来证明自己是人类。因为机器人和计算机程序无法像人类一样理…

[论文笔记] Megtron_LM 0、报错:vscode调试无法传进去参数 launch.json文件获取args参数

解决方法&#xff1a; 配置好launch.json文件后&#xff0c;应该点运行和调试里面的运行按钮。而不是直接点文件右上角的debug。 可以看到terminal中&#xff0c;如果没有正常加载launch.json&#xff0c;则参数中没有args的参数。 如果正常加载&#xff0c;可以看到args的很多…

python+selenium如何做接口自动化测试?

首先&#xff0c; 确定一点我们可以用python和selenium做接口自动化测试&#xff0c; 但不能说只有selenium&#xff0c;因为selenium只是python中的一个包&#xff0c; 他总是和多个包一块综合去运用。要做接口自动化第一步&#xff1a;要把python安装到你的电脑中&#xff0c…

法线贴图可以实现什么样的3D效果

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 在 3D 建模中&#xff0c;曲面由多边形表示。照明计算是基于这些多边…