Android MVVM实践(ViewModel+LiveData+DataBinding)
Andorid常用的开发模式有MVP MVVM,MVP与MVVM不同之处在于MVP中的Presenter持有View层的引用,而MVVM中的ViewModel并未持有View层的引用,而是通过观察者模式完成数据的交互,从而ViewModel层与View层也完成解耦。
这里使用Jetpack组件ViewModel+LiveData+DataBinding完成一个MVVM的例子
目录结构如上所示:
Model层处理网络请求或从数据库获取数据 ;
View层负责UI展示及用户交互;
ViewModel层负责数据的处理及分发;
LoginActivity代码如下
/**
* MVVM中 M为业务层 V为视图层 VM相当于MVP中的P,但是VM不持有V的引用
*/
public class LoginActivity extends AppCompatActivity {
public static void actionStart(Context context) {
Intent intent = new Intent(context, LoginActivity.class);
context.startActivity(intent);
}
private ActivityLoginBinding binding;
private LoginViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_login);
//获取viewModel实例
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication()))
.get(LoginViewModel.class);
binding.setViewModel(viewModel);
//双向绑定需要加这句,否则不起作用
binding.setLifecycleOwner(this);
initListener();
}
private void initListener() {
//相当于网络请求回调,观察回调数据变化
viewModel.getLoginResponseLD().observe(LoginActivity.this, loginResponse -> {
boolean loginSuccess = loginResponse.isLoginSuccess();
if (loginSuccess) {
Toast.makeText(LoginActivity.this, loginResponse.getMessage(), Toast.LENGTH_SHORT).show();
finish();
} else {
Toast.makeText(LoginActivity.this, loginResponse.getMessage(), Toast.LENGTH_SHORT).show();
}
});
binding.btnLogin.setOnClickListener(v -> {
String loginName = viewModel.getLoginNameLD().getValue();
String pwd = viewModel.getPwdLD().getValue();
if (TextUtils.isEmpty(loginName)) {
Toast.makeText(LoginActivity.this, "请输入用户名", Toast.LENGTH_SHORT).show();
return;
}
if (TextUtils.isEmpty(pwd)) {
Toast.makeText(LoginActivity.this, "请输入密码", Toast.LENGTH_SHORT).show();
return;
}
//请求登录
viewModel.login();
});
}
}
布局文件代码,需转化为data binding layout,引入ViewModel完成双向绑定
<?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>
<variable
name="viewModel"
type="com.example.mvvmdemo.viewmodel.LoginViewModel" />
<import type="android.view.View" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.LoginActivity">
<EditText
android:id="@+id/et_login_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入用户名"
android:text="@={viewModel.loginNameLD}"
android:textColor="@color/black"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/et_pwd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入密码"
android:inputType="textPassword"
android:text="@={viewModel.pwdLD}"
android:textColor="@color/black"
app:layout_constraintTop_toBottomOf="@id/et_login_name" />
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录"
app:layout_constraintTop_toBottomOf="@id/et_pwd" />
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{viewModel.loadingLD?View.VISIBLE:View.GONE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
LoginViewModel代码
/**
* @author xushibin
* @date 12/20/21
* description:
*/
public class LoginViewModel extends AndroidViewModel {
public LoginViewModel(@NonNull Application application) {
super(application);
}
private MutableLiveData<String> loginNameLD = new MutableLiveData<>();
private MutableLiveData<String> pwdLD = new MutableLiveData<>();
private MutableLiveData<Boolean> loadingLD = new MutableLiveData<>();
private MutableLiveData<LoginResponse> loginResponseLD = new MutableLiveData<>();
public MutableLiveData<String> getLoginNameLD() {
return loginNameLD;
}
public void setLoginNameLD(MutableLiveData<String> loginNameLD) {
this.loginNameLD = loginNameLD;
}
public MutableLiveData<String> getPwdLD() {
return pwdLD;
}
public void setPwdLD(MutableLiveData<String> pwdLD) {
this.pwdLD = pwdLD;
}
public MutableLiveData<Boolean> getLoadingLD() {
return loadingLD;
}
public void setLoadingLD(MutableLiveData<Boolean> loadingLD) {
this.loadingLD = loadingLD;
}
public MutableLiveData<LoginResponse> getLoginResponseLD() {
return loginResponseLD;
}
public void setLoginResponseLD(MutableLiveData<LoginResponse> loginResponseLD) {
this.loginResponseLD = loginResponseLD;
}
public void login() {
loadingLD.setValue(true);
HttpRequest.login(loginNameLD.getValue(), pwdLD.getValue(), new CallBackLis() {
@Override
public void success(String backStr) {
loadingLD.setValue(false);
LoginResponse loginResponse = new LoginResponse();
loginResponse.setLoginSuccess(true);
loginResponse.setMessage("登录成功!");
loginResponseLD.setValue(loginResponse);
}
@Override
public void failure(int errorCode, String error) {
loadingLD.setValue(false);
LoginResponse loginResponse = new LoginResponse();
loginResponse.setLoginSuccess(false);
loginResponse.setMessage(error);
loginResponseLD.setValue(loginResponse);
}
});
}
}
HttpRequest代码,模拟网络请求操作
/**
* @author xushibin
* @date 12/20/21
* description:
*/
public class HttpRequest {
/**
* 模拟网络请求操作 example/123456登录成功
*
* @param loginName
* @param pwd
* @param callBackLis
*/
public static void login(String loginName, String pwd, CallBackLis callBackLis) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (TextUtils.equals(loginName, "example") && TextUtils.equals(pwd, "123456")) {
callBackLis.success("登录成功");
} else {
callBackLis.failure(101, "登录失败");
}
}
});
}
}).start();
}
}
最后附上完整demo,点击查看https://github.com/xsb0426/MVVMDemo
版权声明:本文为u013624014原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END