当前位置: 首页 > news >正文

Android MVVM架构学习——ViewModel DataBinding

关于MVVM架构,我并不想花篇幅去做重复性的描述,网上一搜都是一堆讲解,大家可以自行了解,我所做的只是以最简单的例子,最有效的步骤,从零开始,去实现一个相对有点学习参考价值的项目。

先来看本文预计的实现效果

可以看到,就是一个非常简单的例子,当点击登录按钮之后,对用户的输入进行一个简单的判断,满足要求之后跳转到首页,并显示用户输入的账户信息。那么接下来,将分步骤讲解如何以符合MVVM设计规范的代码来实现这个功能,重在展示如何从零开始,构建一个MVVM框架。

本文使用的开发环境:

         Android Studio Iguana | 2023.2.1 Patch 1

Gradle版本:

        gradle-8.4-bin.zip 

1.build.gradle文件(模块级)

1.1使用DataBinding
defaultConfig {...buildFeatures {dataBinding = true}...}
1.2 引用依赖
dependencies {implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'implementation 'androidx.lifecycle:lifecycle-livedata:2.7.0'}

 2.绘制布局

当我们新建项目或者是新建activity时,系统会默认为我们生成一个布局文件,如下

我们需要把默认布局改成DataBinding布局。选中根部局标签,按下Alt+Enter,在弹出的选项中,选择第一个Convert to data binding layout,系统会自动为我们修改布局

修改后的布局:

<?xml version="1.0" encoding="utf-8"?>
<!--使用databinding功能,根布局需要使用<layout>标签 -->
<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 Binding的<data>标签,用于定义布局中使用的数据对象和表达式--><data></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".ui.main.MainActivity"></androidx.constraintlayout.widget.ConstraintLayout></layout>

3.Activity文件

/*** 登录活动类,负责展示登录界面并处理登录逻辑。*/
public class LoginActivity extends AppCompatActivity {private ActivityLoginBinding binding; // 视图绑定对象private LoginViewModel viewModel; // 登录视图模型/*** 在活动创建时调用,用于初始化界面和设置监听器。* * @param savedInstanceState 如果活动之前被销毁,这参数包含之前的状态。如果活动没被销毁之前,这参数是null。*/@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 启用边缘到边缘的界面显示EdgeToEdge.enable(this);// 使用数据绑定初始化视图binding = DataBindingUtil.setContentView(this, R.layout.activity_login);// 设置视图嵌入系统边界的监听,用于动态设置视图的内边距ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});// 创建或获取登录视图模型viewModel = new ViewModelProvider(this).get(LoginViewModel.class);// 将视图模型绑定到视图binding.setViewModel(viewModel);// 初始化点击监听器和观察者initListener();initObserver();}/*** 初始化按钮监听器,用于处理登录按钮的点击事件。*/private void initListener() {// 当登录按钮被点击时,设置账号和密码,并触发登录动作binding.btnLogin.setOnClickListener(v -> {viewModel.setAccount(binding.etAccount.getText().toString());viewModel.setPassword(binding.etPassword.getText().toString());viewModel.login();});}/*** 初始化观察者,用于处理登录结果。*/private void initObserver() {// 观察登录结果,根据结果进行跳转或显示错误信息viewModel.getLoginResult().observe(this, loginResult -> {if (loginResult.isSuccess()) {// 登录成功,跳转到主界面,并传递账号信息Intent intent = new Intent(this, MainActivity.class);intent.putExtra("account", viewModel.getAccount().getValue());startActivity(intent);finish();} else {// 登录失败,显示错误信息Toast.makeText(this, loginResult.getErrorMessage(), Toast.LENGTH_SHORT).show();}});}
}

4.定义ViewModel

比较好的编程规范是,每创建一个Activity/Fragment,都创建与其对应的ViewModel

/*** 登录视图模型类,用于管理登录相关的数据和逻辑。*/
public class LoginViewModel extends ViewModel {// 账户名和密码的LiveData对象,用于在UI变化时通知订阅者private MutableLiveData<String> account = new MutableLiveData<>();private MutableLiveData<String> password = new MutableLiveData<>();private MutableLiveData<LoginResult> loginResult = new MutableLiveData<>();/*** 获取账户名的LiveData对象。* @return 账户名的LiveData对象。*/public MutableLiveData<String> getAccount() {return account;}/*** 获取密码的LiveData对象。* @return 密码的LiveData对象。*/public MutableLiveData<String> getPassword() {return password;}/*** 获取登录结果的LiveData对象。* @return 登录结果的LiveData对象。*/public LiveData<LoginResult> getLoginResult() {return loginResult;}/*** 设置账户名。* @param account 用户输入的账户名。*/public void setAccount(String account) {this.account.postValue(account);}/*** 设置密码。* @param password 用户输入的密码。*/public void setPassword(String password) {this.password.postValue(password);}/*** 执行登录操作。* 根据输入的账户名和密码进行校验,成功则更新登录结果为成功,失败则更新为错误信息。*/public void login() {if (checkAccount(getAccount().getValue(), getPassword().getValue())) {LoginResult successResult = new LoginResult(true, null);loginResult.postValue(successResult);} else {LoginResult errorResult = new LoginResult(false, "账号或密码错误");loginResult.postValue(errorResult);}}/*** 校验账户名和密码是否有效。* @param account 用户输入的账户名。* @param password 用户输入的密码。* @return 如果账户名和密码有效返回true,否则返回false。*/private boolean checkAccount(String account, String password) {if (account == null || password == null || account.isEmpty() || password.isEmpty()) {return false;}return true;}/*** 登录结果类,封装登录是否成功和错误信息。*/public static class LoginResult {private boolean success;private String errorMessage;/*** 构造登录结果对象。* @param success 登录是否成功。* @param errorMessage 错误信息,登录失败时提供。*/public LoginResult(boolean success, String errorMessage) {this.success = success;this.errorMessage = errorMessage;}/*** 判断登录是否成功。* @return 登录成功返回true,失败返回false。*/public boolean isSuccess() {return success;}/*** 设置登录是否成功。* @param success 设置登录成功状态。*/public void setSuccess(boolean success) {this.success = success;}/*** 获取错误信息。* @return 错误信息字符串,登录成功时为null。*/public String getErrorMessage() {return errorMessage;}/*** 设置错误信息。* @param errorMessage 设置登录失败的错误信息。*/public void setErrorMessage(String errorMessage) {this.errorMessage = errorMessage;}}}

5.MainActivity

/*** 主活动类,负责管理应用程序的主要界面。*/
public class MainActivity extends AppCompatActivity {private MainViewModel viewModel; // 视图模型,用于管理活动背后的业务逻辑private ActivityMainBinding binding; // 数据绑定实例,用于简化UI更新/*** 在活动创建时调用。* @param savedInstanceState 如果活动之前被销毁,这参数包含之前的状态。如果活动没被销毁之前,这参数是null。*/@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 启用边缘到边缘的UIEdgeToEdge.enable(this);// 设置数据绑定binding = DataBindingUtil.setContentView(this, R.layout.activity_main);// 设置视图的内边距,以适应系统栏位的高度ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});// 初始化视图模型viewModel = new ViewModelProvider(this).get(MainViewModel.class);// 从意图中获取账户信息Intent intent = getIntent();String account = intent.getStringExtra("account");// 将账户信息显示在文本视图上binding.text.setText("登录账户为:"+account);}
}

至此,就完成了demo中展示的效果

相关文章:

Android MVVM架构学习——ViewModel DataBinding

关于MVVM架构&#xff0c;我并不想花篇幅去做重复性的描述&#xff0c;网上一搜都是一堆讲解&#xff0c;大家可以自行了解&#xff0c;我所做的只是以最简单的例子&#xff0c;最有效的步骤&#xff0c;从零开始&#xff0c;去实现一个相对有点学习参考价值的项目。 先来看本…...

防抖与节流

...

理解 Nginx 的多站点配置:为每个网站单独配置

Nginx 是一个高性能的 Web 服务器&#xff0c;广泛用于托管和管理网站。它之所以受欢迎&#xff0c;部分原因在于它的灵活性和强大的配置能力。特别是对于管理多个网站&#xff0c;Nginx 提供了一种高效且组织良好的方法。让我们逐步了解如何使用 Nginx 配置多个网站&#xff0…...

支持向量机模型pytorch

通过5个条件判定一件事情是否会发生&#xff0c;5个条件对这件事情是否发生的影响力不同&#xff0c;计算每个条件对这件事情发生的影响力多大&#xff0c;写一个支持向量机模型pytorch程序,最后打印5个条件分别的影响力。 示例一 支持向量机&#xff08;SVM&#xff09;是一种…...

轮转数组(力扣)

189. 轮转数组 - 力扣&#xff08;LeetCode&#xff09; 189. 轮转数组 题解 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 样例输入 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮…...

批量插入10w数据方法对比

环境准备(mysql5.7) CREATE TABLE user (id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 唯一id,user_id bigint(10) DEFAULT NULL COMMENT 用户id-uuid,user_name varchar(100) NOT NULL COMMENT 用户名,user_age bigint(10) DEFAULT NULL COMMENT 用户年龄,create_time time…...

HAL STM32 I2C方式读取MT6701磁编码器获取角度例程

HAL STM32 I2C方式读取MT6701磁编码器获取角度例程 &#x1f4cd;相关篇《Arduino通过I2C驱动MT6701磁编码器并读取角度数据》&#x1f388;《STM32 软件I2C方式读取MT6701磁编码器获取角度例程》&#x1f4cc;MT6701当前最新文档资料&#xff1a;https://www.magntek.com.cn/u…...

如何排查nginx服务启动情况,杀死端口,以及防火墙开放指定端口【linux与nginx排查手册】

利用NGINX搭建了视频服务&#xff0c;突然发现启动不了了&#xff0c;于是命令开始 使用以下命令查看更详细的错误信息&#xff1a; systemctl status nginx.service Warning: The unit file, source configuration file or drop-ins of nginx.service changed on disk. Run…...

用Rust实现免费调用ChatGPT的命令行工具 (一)

代码已经开源&#xff1a;&#x1f680; fgpt 欢迎大家star⭐和fork &#x1f44f; ChatGPT现在免费提供了GPT3.5的Web访问&#xff0c;不需要注册就可以直接使用&#xff0c;但是&#xff0c;它的使用方式是通过Web页面&#xff0c;不够方便。 更多技术分享关注 入职啦&…...

mysql 查询实战1-题目

学习了mysql 查询实战-变量方式-解答-CSDN博客&#xff0c;接着练习sql&#xff0c;从实战中多练习。 1&#xff0c;题目&#xff1a; 1&#xff0c;查询部门工资最高的员工 1&#xff0c;建表&#xff1a; DROP TABLE IF EXISTS department; create table department(dept_i…...

Word学习笔记之奇偶页的页眉与页码设置

1. 常用格式 在毕业论文中&#xff0c;往往有一下要求&#xff1a; 奇数页右下角显示、偶数页左下角显示奇数页眉为每章标题、偶数页眉为论文标题 2. 问题解决 2.1 前期准备 首先&#xff0c;不论时要求 1、还是要求 2&#xff0c;这里我们都要做一下设置&#xff1a; 鼠…...

数据赋能(58)——要求:数据赋能实施部门能力

“要求&#xff1a;数据赋能实施部门能力”是作为标准的参考内容编写的。 在实施数据赋能中&#xff0c;数据赋能实施部门的能力体现在多个方面&#xff0c;关键能力如下图所示。 在实施数据赋能的过程中&#xff0c;数据赋能实施部门应具备的关键能力如下。 理性思维与逻辑分…...

Unity URP PBR_Cook-Torrance模型

Cook-Torrance模型是一个微表面光照模型&#xff0c;认为物体的表面可以看作是由许多个理想的镜面反射体微小平面组成的。 单点反射镜面反射漫反射占比*漫反射 漫反射 基础色/Π 镜面反射DFG/4(NV)(NL) D代表微平面分布函数&#xff0c;描述的是法线与半角向量normalize(L…...

Unity之XR Interaction Toolkit如何在VR中实现渐变黑屏效果

前言 做VR的时候,有时会有跳转场景,切换位置,切换环境,切换进度等等需求,此时相机的画面如果不切换个黑屏,总会感觉很突兀。刚好Unity的XR Interaction Toolkit插件在2.5.x版本,出了一个TunnelingVignette的效果,我们今天就来分析一下他是如何使用的,然后我们自己再来…...

html+vue编写分页功能

效果&#xff1a; html关键代码&#xff1a; <div class"ui-jqgrid-resize-mark" id"rs_mlist_table_C87E35BE"> </div><div class"list_component_pager ui-jqgrid-pager undefined" dir"ltr"><div id"pg…...

计算机网络 实验指导 实验17

实验17 配置无线网络实验 1.实验拓扑图 Table PC0 和 Table PC1 最开始可能还会连Access Point0&#xff0c;无影响后面会改 名称接口IP地址网关地址Router0fa0/0210.10.10.1fa0/1220.10.10.2Tablet PC0210.10.10.11Tablet PC1210.10.10.12Wireless互联网220.10.10.2LAN192.16…...

在 Vue中,v-for 指令的使用

在 Vue中&#xff0c;v-for 指令用于渲染一个列表&#xff0c;基于源数据多次渲染元素或模板块。它对于展示数组或对象中的数据特别有用。 数组渲染 假设你有一个数组&#xff0c;并且你想为每个数组元素渲染一个 <li> 标签&#xff1a; <template> <ul>…...

达梦数据库执行sql报错:数据溢出

数据库执行sql报错数据溢出 单独查询对应的数字进行计算是不是超过了某个字段类型的上限或下限 如果已经超过了&#xff0c;进行对字段进行cast类型转换处理&#xff0c;转换为dec num都可以尝试 这里就是从 max(T.BLOCK_ID as dec*8192t.bytes)/1024/1024 max_MB,换成了这个…...

从「宏大叙事」到「生活叙事」,小红书品牌种草的的“正确姿势”

不同于抖音和微博&#xff0c;在小红书上&#xff0c;品牌营销的基调应该是怎样的&#xff1f;品牌怎样与小红书用户对话&#xff1f;什么样的内容&#xff0c;才能走进小红书用户的心中&#xff1f;本期&#xff0c;小编将带大家洞察品牌在小红书营销的“正确姿势”。从「小美…...

Python Selenium 的基本使用方法

文章目录 1. 概述2. 安装Chrome及ChromeDriver2.1 安装Chrome2.2 安装ChromeDriver 3. 安装Selenium4. 常见用法4.1 启动4.2 查找元素4.3 等待页面加载元素 1. 概述 Selenium 是一个用于自动化 web 浏览器的工具&#xff0c;它提供了一套用于测试 web 应用程序的工具和库。Sel…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

Spring Boot + MyBatis 集成支付宝支付流程

Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例&#xff08;电脑网站支付&#xff09; 1. 添加依赖 <!…...