关于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文件
/*** LoginActivity 类负责处理登录界面的逻辑。* 它扩展了 AppCompatActivity,并使用 DataBinding 和 ViewModel 来简化界面和数据的交互。*/
public class LoginActivity extends AppCompatActivity {private ActivityLoginBinding binding; // 用于存放 DataBinding 实例private LoginViewModel viewModel; // 用于存放 LoginViewModel 实例/*** onCreate 方法在活动创建时被调用。* 它负责初始化界面、设置窗口边距和绑定数据。** @param savedInstanceState 如果活动之前被销毁,这参数包含之前的状态。如果活动没被销毁之前,这参数是 null。*/@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 启用全面屏显示EdgeToEdge.enable(this);// 使用 DataBinding 来绑定布局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;});// 创建并获取 LoginViewModel 实例viewModel = new ViewModelProvider(this).get(LoginViewModel.class);// 将 viewModel 绑定到布局上binding.setViewModel(viewModel);// 初始化监听器initListener();}/*** 初始化各种监听器,特别是登录按钮的点击事件。*/private void initListener(){// 设置登录按钮的点击事件,收集用户输入并调用 viewModel 的 login 方法binding.btnLogin.setOnClickListener(v -> {viewModel.setAccount(binding.etAccount.getText().toString());viewModel.setPassword(binding.etPassword.getText().toString());viewModel.login();});}
}
4.定义ViewModel
比较好的编程规范是,每创建一个Activity/Fragment,都创建与其对应的ViewModel
/*** 登录视图模型类,用于管理登录相关的数据和逻辑。*/
public class LoginViewModel extends ViewModel {// 账户名和密码的LiveData对象,用于在UI变化时通知订阅者private MutableLiveData<String> account = new MutableLiveData<>();private MutableLiveData<String> password = new MutableLiveData<>();/*** 获取账户名的LiveData对象。* @return 账户名的LiveData对象。*/public MutableLiveData<String> getAccount() {return account;}/*** 获取密码的LiveData对象。* @return 密码的LiveData对象。*/public MutableLiveData<String> getPassword() {return password;}/*** 设置账户名。* @param account 用户输入的账户名。*/public void setAccount(String account) {this.account.postValue(account);}/*** 设置密码。* @param password 用户输入的密码。*/public void setPassword(String password) {this.password.postValue(password);}/*** 执行登录操作,展示登录成功信息。* 该方法会将当前设置的账户名和密码通过Toast消息展示出来。*/public void login(){//登录操作,例如demo中的展示Toast,但是展示Toast的操作应该放在View层执行,不可以在此处直接调用Toast!}}
5.自定义Application
拆分ViewModel之后,将无法再从Activity中获取Context了,所以自定义Application,便于全局获取Context
public class MVVMApplication extends Application {private static MVVMApplication instance;public static MVVMApplication getInstance() {return instance;}@Overridepublic void onCreate() {super.onCreate();instance = this;}
}
不要忘了manifest文件中声明一下
<applicationandroid:name=".MVVMApplication"
...