关于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.ConstraintLayout
android: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。
*/
@Override
protected 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;
}
@Override
public void onCreate() {
super.onCreate();
instance = this;
}
}
不要忘了manifest文件中声明一下
<application
android:name=".MVVMApplication"
...