我们已经使用Data Binding](/community/tutorials/android-mvvm-design-pattern)实现了[MVVM,并在单独的教程中介绍了LiveData和Data Binding]。今天,我们将在我们的MVVM Android应用程序中使用LiveData和数据绑定。我们将看到LiveData如何轻松地从ViewModel更新用户界面。
MVVM LiveData数据绑定
到目前为止,我们已经使用数据绑定从ViewModel更新了View。LiveData是一个方便的数据容器,它充当要传递的数据的容器。LiveData最好的一点是它是生命周期感知的。因此,如果您在后台,用户界面不会尝试更新。这使我们在运行时避免了大量的崩溃。我们将使用MuableLiveData类,因为它提供了公共方法setValue()
和getValue()
。让我们使用以上概念创建一个简单的登录应用程序。我们将首先使用LiveData和双向数据绑定,然后将数据绑定可观测对象完全重构为LiveData。
入门
在你的应用的build.gradle中添加以下依赖项:
1android {
2 ...
3
4 dataBinding {
5 enabled = true
6 }
7 ...
8}
9
10dependencies {
11 ...
12 implementation 'android.arch.lifecycle:extensions:1.1.1'
13 implementation 'com.android.support:design:28.0.0-beta01'
14 ...
15}
项目结构
LoginViewModelOld文件将包含旧代码,LoginViewModel文件将包含重构代码。
机型
我们已经在User.Java类中定义了我们的模型:
1package com.journaldev.androidmvvmdatabindinglivedata;
2
3import android.util.Patterns;
4
5public class User {
6
7 private String mEmail;
8 private String mPassword;
9
10 public User(String email, String password) {
11 mEmail = email;
12 mPassword = password;
13 }
14
15 public String getEmail() {
16 if (mEmail == null) {
17 return "";
18 }
19 return mEmail;
20 }
21
22 public String getPassword() {
23
24 if (mPassword == null) {
25 return "";
26 }
27 return mPassword;
28 }
29
30 public boolean isEmailValid() {
31 return Patterns.EMAIL_ADDRESS.matcher(getEmail()).matches();
32 }
33
34 public boolean isPasswordLengthGreaterThan5() {
35 return getPassword().length() > 5;
36 }
37
38}
布局
Active_main.xml的代码如下所示:
1<?xml version="1.0" encoding="utf-8"?>
2<layout xmlns:android="https://schemas.android.com/apk/res/android"
3 xmlns:app="https://schemas.android.com/apk/res-auto">
4
5 <data>
6
7 <variable
8 name="loginViewModel"
9 type="com.journaldev.androidmvvmdatabindinglivedata.LoginViewModel" />
10 </data>
11
12 <ScrollView
13 android:layout_width="match_parent"
14 android:layout_height="match_parent">
15
16 <LinearLayout
17 android:layout_width="match_parent"
18 android:layout_height="wrap_content"
19 android:layout_gravity="center"
20 android:layout_margin="8dp"
21 android:orientation="vertical">
22
23 <android.support.design.widget.TextInputLayout
24 android:layout_width="match_parent"
25 android:layout_height="wrap_content"
26 app:error="@{loginViewModel.errorEmail}"
27 app:errorEnabled="true">
28
29 <EditText
30 android:id="@+id/inEmail"
31 android:layout_width="match_parent"
32 android:layout_height="wrap_content"
33 android:hint="Email"
34 android:inputType="textEmailAddress"
35 android:padding="8dp"
36 android:text="@={loginViewModel.email}" />
37
38 </android.support.design.widget.TextInputLayout>
39
40 <android.support.design.widget.TextInputLayout
41 android:layout_width="match_parent"
42 android:layout_height="wrap_content"
43 app:error="@{loginViewModel.errorPassword}"
44 app:errorEnabled="true">
45
46 <EditText
47 android:id="@+id/inPassword"
48 android:layout_width="match_parent"
49 android:layout_height="wrap_content"
50 android:hint="Password"
51 android:inputType="textPassword"
52 android:padding="8dp"
53 android:text="@={loginViewModel.password}" />
54
55 </android.support.design.widget.TextInputLayout>
56
57 <Button
58 android:id="@+id/button"
59 android:layout_width="match_parent"
60 android:layout_height="wrap_content"
61 android:layout_marginTop="8dp"
62 android:onClick="@{()-> loginViewModel.onLoginClicked()}"
63 android:text="LOGIN" />
64
65 <ProgressBar
66 android:id="@+id/progressBar"
67 style="?android:attr/progressBarStyleLarge"
68 android:layout_width="wrap_content"
69 android:layout_height="wrap_content"
70 android:layout_gravity="center_horizontal"
71 android:layout_marginTop="8dp"
72 android:visibility="@{loginViewModel.busy}" />
73
74 </LinearLayout>
75
76 </ScrollView>
77
78</layout>
将显示ProgressBar以模拟登录功能。
ViewModel
LoginViewModel.java的代码如下:
1package com.journaldev.androidmvvmdatabindinglivedata;
2
3import android.arch.lifecycle.LiveData;
4import android.arch.lifecycle.MutableLiveData;
5import android.databinding.BaseObservable;
6import android.databinding.Bindable;
7import android.databinding.ObservableField;
8import android.os.Handler;
9import android.support.annotation.NonNull;
10
11public class LoginViewModel extends BaseObservable {
12 private String email;
13 private String password;
14 private int busy = 8;
15 public final ObservableField<String> errorPassword = new ObservableField<>();
16 public final ObservableField<String> errorEmail = new ObservableField<>();
17
18 public LoginViewModel() {
19 }
20
21 private MutableLiveData<User> userMutableLiveData;
22
23 LiveData<User> getUser() {
24 if (userMutableLiveData == null) {
25 userMutableLiveData = new MutableLiveData<>();
26 }
27
28 return userMutableLiveData;
29 }
30
31 @Bindable
32 @NonNull
33 public String getEmail() {
34 return this.email;
35 }
36
37 public void setEmail(@NonNull String email) {
38 this.email = email;
39 notifyPropertyChanged(BR.email);
40 }
41
42 @Bindable
43 @NonNull
44 public String getPassword() {
45 return this.password;
46 }
47
48 public void setPassword(@NonNull String password) {
49 this.password = password;
50 notifyPropertyChanged(BR.password);
51 }
52
53 @Bindable
54 public int getBusy() {
55 return this.busy;
56 }
57
58 public void setBusy(int busy) {
59 this.busy = busy;
60 notifyPropertyChanged(BR.busy);
61 }
62
63 public void onLoginClicked() {
64
65 setBusy(0); //View.VISIBLE
66 new Handler().postDelayed(new Runnable() {
67 @Override
68 public void run() {
69
70 User user = new User(getEmail(), getPassword());
71
72 if (!user.isEmailValid()) {
73 errorEmail.set("Enter a valid email address");
74 } else {
75 errorEmail.set(null);
76 }
77
78 if (!user.isPasswordLengthGreaterThan5())
79 errorPassword.set("Password Length should be greater than 5");
80 else {
81 errorPassword.set(null);
82 }
83
84 userMutableLiveData.setValue(user);
85 setBusy(8); //8 == View.GONE
86
87 }
88 }, 5000);
89 }
90}
ObservableField是一个对象包装器,使其可观察。在上面的代码中,我们将用户封装在一个LiveData中。每次更改用户对象时,都会在MainActivity中观察到它,并采取适当的操作。当该按钮被单击时,我们将ProgressBar设置为可见。View.VISIBLE=0
。View.GONE==8
延迟5秒后,会验证邮箱和密码,并更新TextInputLayout绑定。
ObservableField不支持生命周期。
MainActivity.Java类如下所示:
1package com.journaldev.androidmvvmdatabindinglivedata;
2
3import android.arch.lifecycle.Observer;
4import android.databinding.DataBindingUtil;
5import android.support.annotation.Nullable;
6import android.support.v7.app.AppCompatActivity;
7import android.os.Bundle;
8import android.widget.Toast;
9
10import com.journaldev.androidmvvmdatabindinglivedata.databinding.ActivityMainBinding;
11
12public class MainActivity extends AppCompatActivity {
13
14 @Override
15 protected void onCreate(Bundle savedInstanceState) {
16 super.onCreate(savedInstanceState);
17
18 ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
19 LoginViewModel loginViewModel = new LoginViewModel();
20 binding.setLoginViewModel(loginViewModel);
21
22 loginViewModel.getUser().observe(this, new Observer() {
23 @Override
24 public void onChanged(@Nullable User user) {
25 if (user.getEmail().length() > 0 || user.getPassword().length() > 0)
26 Toast.makeText(getApplicationContext(), "email : " + user.getEmail() + " password " + user.getPassword(), Toast.LENGTH_SHORT).show();
27 }
28 });
29
30 }
31}
在上面的代码中,Obview方法查找MuableLiveData中包含的User对象中的更改。它显示一个带有用户名和密码的祝酒词。现在,让我们用LiveData完全替换ObservableField.
将Observablefield重构为LiveData
新的LoginViewModel.Java类的代码如下所示:
1package com.journaldev.androidmvvmdatabindinglivedata;
2
3import android.arch.lifecycle.LiveData;
4import android.arch.lifecycle.MutableLiveData;
5import android.arch.lifecycle.ViewModel;
6import android.os.Handler;
7
8public class LoginViewModel extends ViewModel {
9
10 public MutableLiveData<String> errorPassword = new MutableLiveData<>();
11 public MutableLiveData<String> errorEmail = new MutableLiveData<>();
12
13 public MutableLiveData<String> email = new MutableLiveData<>();
14 public MutableLiveData<String> password = new MutableLiveData<>();
15 public MutableLiveData<Integer> busy;
16
17 public MutableLiveData<Integer> getBusy() {
18
19 if (busy == null) {
20 busy = new MutableLiveData<>();
21 busy.setValue(8);
22 }
23
24 return busy;
25 }
26
27 public LoginViewModel() {
28
29 }
30
31 private MutableLiveData<User> userMutableLiveData;
32
33 LiveData<User> getUser() {
34 if (userMutableLiveData == null) {
35 userMutableLiveData = new MutableLiveData<>();
36 }
37
38 return userMutableLiveData;
39 }
40
41 public void onLoginClicked() {
42
43 getBusy().setValue(0); //View.VISIBLE
44 new Handler().postDelayed(new Runnable() {
45 @Override
46 public void run() {
47
48 User user = new User(email.getValue(), password.getValue());
49
50 if (!user.isEmailValid()) {
51 errorEmail.setValue("Enter a valid email address");
52 } else {
53 errorEmail.setValue(null);
54 }
55
56 if (!user.isPasswordLengthGreaterThan5())
57 errorPassword.setValue("Password Length should be greater than 5");
58 else {
59 errorPassword.setValue(null);
60 }
61
62 userMutableLiveData.setValue(user);
63 busy.setValue(8); //8 == View.GONE
64
65 }
66 }, 3000);
67 }
68}
上面的类现在扩展了ViewModel,因为我们不再需要BaseObservable
。现在,我们已经将ObservableFields更改为MuableLiveData。由于数据绑定,MuableLiveData中的更改将在布局中自动更新。我们的MainActivity.Java类现在更新为:
1package com.journaldev.androidmvvmdatabindinglivedata;
2
3import android.arch.lifecycle.Observer;
4import android.arch.lifecycle.ViewModelProviders;
5import android.databinding.DataBindingUtil;
6import android.support.annotation.Nullable;
7import android.support.v7.app.AppCompatActivity;
8import android.os.Bundle;
9import android.widget.Toast;
10
11import com.journaldev.androidmvvmdatabindinglivedata.databinding.ActivityMainBinding;
12
13public class MainActivity extends AppCompatActivity {
14
15 @Override
16 protected void onCreate(Bundle savedInstanceState) {
17 super.onCreate(savedInstanceState);
18
19 ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
20 LoginViewModel loginViewModel = ViewModelProviders.of(this).get(LoginViewModel.class);
21 binding.setLoginViewModel(loginViewModel);
22 binding.setLifecycleOwner(this);
23
24 loginViewModel.getUser().observe(this, new Observer() {
25 @Override
26 public void onChanged(@Nullable User user) {
27 if (user.getEmail().length() > 0 || user.getPassword().length() > 0)
28 Toast.makeText(getApplicationContext(), "email : " + user.getEmail() + " password " + user.getPassword(), Toast.LENGTH_SHORT).show();
29 }
30 });
31
32 }
33}
ViewModelProviders.of 也可用于创建ViewModel实例,如上所述。此方法仅实例化一次** 一次** 的ViewModel。每次后续调用都会** 重用** 该实例。LifecycleOwner是我们的活动可以绑定到的接口。上述应用程序的实际输出如下:如您所见,当用户离开应用程序时不会显示Toast消息。因为LiveData是生命周期感知的。当您再次打开该应用程序时,将显示祝酒词。本教程到此结束。您可以从下面的链接下载该项目:
[Android idMVMDataBindingLiveData](https://Jouraldev.nyc3.digitaloceanspaces.com/2018/08/androidMVM DataBindingLiveData.zip)