Android MVVM LiveData Data Binding 在Android中的应用
我们已经使用Data Binding实现了MVVM,并在单独的教程中介绍了LiveData和Data Binding。今天,我们将在我们的MVVM Android应用程序中使用LiveData与Data Binding。我们将看到LiveData如何使得从ViewModel更新UI变得容易。
MVVM (Model-View-ViewModel)LiveData(实时数据)Data Binding(数据绑定)
到目前为止,我们一直在使用数据绑定来从ViewModel更新视图。LiveData是一个方便的数据容器,作为要传递的数据的容器。LiveData最好的是它具有生命周期感知功能。所以如果您在后台,界面就不会尝试更新。这样可以避免很多运行时崩溃。我们将使用MutableLiveData类,因为它提供了公共方法setValue()和getValue()。让我们使用上述概念创建一个简单的登录应用程序。首先,我们将使用LiveData以及双向数据绑定,然后彻底将数据绑定的可观察对象重构为LiveData。
开始使用
在您应用程序的build.gradle中添加以下依赖项。
android {
...
dataBinding {
enabled = true
}
...
}
dependencies {
...
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'com.android.support:design:28.0.0-beta01'
...
}
项目结构
LoginViewModelOld文件中将包含旧的代码,而LoginViewModel文件将包含重构后的代码。
模型
我们在User.java类中定义了我们的模型。
package com.Olivia.androidmvvmdatabindinglivedata;
import android.util.Patterns;
public class User {
private String mEmail;
private String mPassword;
public User(String email, String password) {
mEmail = email;
mPassword = password;
}
public String getEmail() {
if (mEmail == null) {
return "";
}
return mEmail;
}
public String getPassword() {
if (mPassword == null) {
return "";
}
return mPassword;
}
public boolean isEmailValid() {
return Patterns.EMAIL_ADDRESS.matcher(getEmail()).matches();
}
public boolean isPasswordLengthGreaterThan5() {
return getPassword().length() > 5;
}
}
布局
以下是activity_main.xml的代码:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto">
<data>
<variable
name="loginViewModel"
type="com.Olivia.androidmvvmdatabindinglivedata.LoginViewModel" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:error="@{loginViewModel.errorEmail}"
app:errorEnabled="true">
<EditText
android:id="@+id/inEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email"
android:inputType="textEmailAddress"
android:padding="8dp"
android:text="@={loginViewModel.email}" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:error="@{loginViewModel.errorPassword}"
app:errorEnabled="true">
<EditText
android:id="@+id/inPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword"
android:padding="8dp"
android:text="@={loginViewModel.password}" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:onClick="@{()-> loginViewModel.onLoginClicked()}"
android:text="LOGIN" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:visibility="@{loginViewModel.busy}" />
</LinearLayout>
</ScrollView>
</layout>
进度条将被显示以模拟登录功能。
视图模型
下面是LoginViewModel.java的代码:
package com.Olivia.androidmvvmdatabindinglivedata;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.databinding.ObservableField;
import android.os.Handler;
import android.support.annotation.NonNull;
public class LoginViewModel extends BaseObservable {
private String email;
private String password;
private int busy = 8;
public final ObservableField<String> errorPassword = new ObservableField<>();
public final ObservableField<String> errorEmail = new ObservableField<>();
public LoginViewModel() {
}
private MutableLiveData<User> userMutableLiveData;
LiveData<User> getUser() {
if (userMutableLiveData == null) {
userMutableLiveData = new MutableLiveData<>();
}
return userMutableLiveData;
}
@Bindable
@NonNull
public String getEmail() {
return this.email;
}
public void setEmail(@NonNull String email) {
this.email = email;
notifyPropertyChanged(BR.email);
}
@Bindable
@NonNull
public String getPassword() {
return this.password;
}
public void setPassword(@NonNull String password) {
this.password = password;
notifyPropertyChanged(BR.password);
}
@Bindable
public int getBusy() {
return this.busy;
}
public void setBusy(int busy) {
this.busy = busy;
notifyPropertyChanged(BR.busy);
}
public void onLoginClicked() {
setBusy(0); //View.VISIBLE
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
User user = new User(getEmail(), getPassword());
if (!user.isEmailValid()) {
errorEmail.set("Enter a valid email address");
} else {
errorEmail.set(null);
}
if (!user.isPasswordLengthGreaterThan5())
errorPassword.set("Password Length should be greater than 5");
else {
errorPassword.set(null);
}
userMutableLiveData.setValue(user);
setBusy(8); //8 == View.GONE
}
}, 5000);
}
}
ObservableField是一个对象包装器,使其可观察。在上述代码中,我们将User封装在LiveData中。每当用户对象发生变化时,它将在MainActivity中被观察,并采取相应的动作。当按钮被点击时,我们将ProgressBar设置为可见。View.VISIBLE = 0。View.GONE == 8 在5秒的延迟后,将验证电子邮件和密码,并更新TextInputLayout绑定项。
ObservableField 不具备生命周期感知能力。(ObservableField 不会感知生命周期。)
以下是MainActivity.java类的代码:
package com.Olivia.androidmvvmdatabindinglivedata;
import android.arch.lifecycle.Observer;
import android.databinding.DataBindingUtil;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
import com.Olivia.androidmvvmdatabindinglivedata.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
LoginViewModel loginViewModel = new LoginViewModel();
binding.setLoginViewModel(loginViewModel);
loginViewModel.getUser().observe(this, new Observer() {
@Override
public void onChanged(@Nullable User user) {
if (user.getEmail().length() > 0 || user.getPassword().length() > 0)
Toast.makeText(getApplicationContext(), "email : " + user.getEmail() + " password " + user.getPassword(), Toast.LENGTH_SHORT).show();
}
});
}
}
在上面的代码中,observe方法会查找包含在MutableLiveData中的User对象的更改。它会显示一个带有用户名和密码的toast。现在,让我们完全用LiveData替换ObservableField。
将ObservableField重构为LiveData。
以下是新的LoginViewModel.java类的代码:
package com.Olivia.androidmvvmdatabindinglivedata;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;
import android.os.Handler;
public class LoginViewModel extends ViewModel {
public MutableLiveData<String> errorPassword = new MutableLiveData<>();
public MutableLiveData<String> errorEmail = new MutableLiveData<>();
public MutableLiveData<String> email = new MutableLiveData<>();
public MutableLiveData<String> password = new MutableLiveData<>();
public MutableLiveData<Integer> busy;
public MutableLiveData<Integer> getBusy() {
if (busy == null) {
busy = new MutableLiveData<>();
busy.setValue(8);
}
return busy;
}
public LoginViewModel() {
}
private MutableLiveData<User> userMutableLiveData;
LiveData<User> getUser() {
if (userMutableLiveData == null) {
userMutableLiveData = new MutableLiveData<>();
}
return userMutableLiveData;
}
public void onLoginClicked() {
getBusy().setValue(0); //View.VISIBLE
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
User user = new User(email.getValue(), password.getValue());
if (!user.isEmailValid()) {
errorEmail.setValue("Enter a valid email address");
} else {
errorEmail.setValue(null);
}
if (!user.isPasswordLengthGreaterThan5())
errorPassword.setValue("Password Length should be greater than 5");
else {
errorPassword.setValue(null);
}
userMutableLiveData.setValue(user);
busy.setValue(8); //8 == View.GONE
}
}, 3000);
}
}
上面的类现在扩展了ViewModel,因为我们不再需要BaseObservable。现在,我们已经将ObservableFields更改为MutableLiveData。由于数据绑定的帮助,MutableLiveData中的更改会自动更新到布局中。我们的MainActivity.java类现在已更新为:
package com.Olivia.androidmvvmdatabindinglivedata;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.databinding.DataBindingUtil;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
import com.Olivia.androidmvvmdatabindinglivedata.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
LoginViewModel loginViewModel = ViewModelProviders.of(this).get(LoginViewModel.class);
binding.setLoginViewModel(loginViewModel);
binding.setLifecycleOwner(this);
loginViewModel.getUser().observe(this, new Observer() {
@Override
public void onChanged(@Nullable User user) {
if (user.getEmail().length() > 0 || user.getPassword().length() > 0)
Toast.makeText(getApplicationContext(), "email : " + user.getEmail() + " password " + user.getPassword(), Toast.LENGTH_SHORT).show();
}
});
}
}
ViewModelProviders.of也可以用于创建ViewModel实例,就像上面那样。这个方法只实例化ViewModel一次。每次后续调用都会重用这个实例。LifecycleOwner是一个我们的Activity可以绑定到的接口。上述应用程序的输出如下所示:正如你所看到的,当用户离开应用程序时,Toast消息不会显示。因为LiveData是感知生命周期的。当你再次打开应用程序时,Toast将会显示。这就结束了本教程。你可以从下面的链接下载这个项目:
安卓MVVM数据绑定LiveData
Github 项目链接