Android MVVM LiveData 数据绑定

我们已经使用Data Binding](/community/tutorials/android-mvvm-design-pattern)实现了[MVVM,并在单独的教程中介绍了LiveDataData 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}

项目结构

android-mvvm-livedata-databinding-projectLoginViewModelOld文件将包含旧代码,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=0View.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是我们的活动可以绑定到的接口。上述应用程序的实际输出如下:Android MVVM live data绑定demo如您所见,当用户离开应用程序时不会显示Toast消息。因为LiveData是生命周期感知的。当您再次打开该应用程序时,将显示祝酒词。本教程到此结束。您可以从下面的链接下载该项目:

[Android idMVMDataBindingLiveData](https://Jouraldev.nyc3.digitaloceanspaces.com/2018/08/androidMVM DataBindingLiveData.zip)

Github项目链接

Published At
Categories with 技术
Tagged with
comments powered by Disqus