关于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文件
/**
* 登录活动类,负责展示登录界面并处理登录逻辑。
*/
public class LoginActivity extends AppCompatActivity {
private ActivityLoginBinding binding; // 视图绑定对象
private LoginViewModel viewModel; // 登录视图模型
/**
* 在活动创建时调用,用于初始化界面和设置监听器。
*
* @param savedInstanceState 如果活动之前被销毁,这参数包含之前的状态。如果活动没被销毁之前,这参数是null。
*/
@Override
protected 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。
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 启用边缘到边缘的UI
EdgeToEdge.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中展示的效果