livedata mvvm
We’ve already implemented MVVM using Data Binding and covered LiveData and Data Binding in separate tutorials. Today, we’ll use LiveData with Data Binding in our MVVM Android Application. We’ll see how LiveData makes it easy to update the UI from the ViewModel.
我们已经使用数据绑定实现了MVVM ,并在单独的教程中介绍了LiveData和Data Binding 。 今天,我们将在MVVM Android应用程序中使用带有数据绑定的LiveData。 我们将看到LiveData如何使从ViewModel更新UI变得容易。
MVVM LiveData数据绑定 (MVVM LiveData Data Binding)
Up until now, we’ve used Data Binding to update the View from the ViewModel. LiveData is a handy data holder that acts as a container over the data to be passed. The best thing about LiveData is that it is lifecycle aware. So if you are in the background, the UI won’t try to update.
到目前为止,我们已经使用数据绑定从ViewModel更新视图。 LiveData是一个方便的数据持有者,充当要传递的数据的容器。 LiveData最好的事情是它具有生命周期意识。 因此,如果您在后台,则UI不会尝试更新。
This saves us from a lot of crashes at runtime.
这使我们免于在运行时发生大量崩溃。
We’ll use the MutableLiveData class since it provides public methods setValue()
and getValue()
.
我们将使用MutableLiveData类,因为它提供了公共方法setValue()
和getValue()
。
Let’s create a simple Login Application using the above concepts. We will first use LiveData as well as Two-way Data Binding and then refactor the Data Binding Observables to LiveData completely.
让我们使用以上概念创建一个简单的Login Application。 我们将首先使用LiveData以及双向数据绑定,然后将数据绑定可观察对象完全重构为LiveData。
入门 (Getting Started)
Add the following dependency in your app’s build.gradle:
在应用程序的build.gradle中添加以下依赖项:
android {
...
dataBinding {
enabled = true
}
...
}
dependencies {
...
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'com.android.support:design:28.0.0-beta01'
...
}
项目结构 (Project Structure)
The LoginViewModelOld file would contain the old code and LoginViewModel file would contain the refactored code.
LoginViewModelOld文件将包含旧代码,而LoginViewModel文件将包含重构代码。
模型 (Model)
We’ve defined our Model in the User.java class:
我们已经在User.java类中定义了模型:
package com.journaldev.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;
}
}
布局 (Layout)
The code for the activity_main.xml is given below:
下面给出了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.journaldev.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>
The ProgressBar would be displayed to simulate the login feature.
将显示ProgressBar以模拟登录功能。
视图模型 (ViewModel)
The code for the LoginViewModel.java is given below:
下面给出了LoginViewModel.java的代码:
package com.journaldev.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);
}
}
An ObservableField is an object wrapper to make it observable.
In the above code, we’ve encapsulated User inside a LiveData. Every time the user object is changed it will be observed in the MainActivity and the appropriate action would be taken.
ObservableField是使它可观察的对象包装。
在上面的代码中,我们将User封装在LiveData中。 每次更改用户对象时,都会在MainActivity中观察到该对象,并会采取适当的措施。
When the button is clicked, we set the ProgressBar to Visible. View.VISIBLE = 0
.
View.GONE == 8
单击按钮后,我们将ProgressBar设置为Visible。 View.VISIBLE = 0
。
View.GONE == 8
After a delay of 5 seconds, the email and password are validated and the TextInputLayout bindables are updated.
延迟5秒后,将验证电子邮件和密码,并更新TextInputLayout可绑定对象。
The MainActivity.java class is given below:
MainActivity.java类如下所示:
package com.journaldev.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.journaldev.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();
}
});
}
}
In the above code, the observe method looks for changes in the User object that was contained in the MutableLiveData. It displays a toast with the username and password.
在上面的代码中,observe方法在MutableLiveData中包含的User对象中查找更改。 它显示带有用户名和密码的吐司。
Now, let’s replace the ObservableField with LiveData completely.
现在,让我们用LiveData完全替换ObservableField。
将ObservableField重构为LiveData (Refactoring ObservableField to LiveData)
The code for the new LoginViewModel.java class is given below:
下面给出了新的LoginViewModel.java类的代码:
package com.journaldev.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);
}
}
The above class now extends ViewModel since we no longer need BaseObservable
.
上面的类现在扩展了ViewModel,因为我们不再需要BaseObservable
。
Now, we’ve changed the ObservableFields to MutableLiveData. Changes in the MutableLiveData would be automatically updated in the layout thanks to Data Binding.
现在,我们将ObservableFields更改为MutableLiveData。 MutableLiveData中的更改将由于数据绑定而在布局中自动更新。
Our MainActivity.java class is now updated to:
我们的MainActivity.java类现在更新为:
package com.journaldev.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.journaldev.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 can be used to create ViewModel instance too as done above.
This method instantiates the ViewModel only once. Every subsequent call would reuse the instance.
如上所述, ViewModelProviders.of也可用于创建ViewModel实例。
此方法仅实例化ViewModel 一次 。 每个后续调用将重用实例。
The LifecycleOwner is an interface that our Activity can bind to.
LifecycleOwner是我们的Activity可以绑定的接口。
The output of the above application in action is given below:
上面应用程序的输出如下:
As you can see, the Toast message isn’t shown when the user leaves the application. Since the LiveData is lifecycle aware. When you open the application again, the toast would be displayed.
如您所见,当用户离开应用程序时,不会显示Toast消息。 由于LiveData是生命周期感知的。 当您再次打开应用程序时,将显示吐司。
This brings an end to this tutorial. You can download the project from the link below:
本教程到此结束。 您可以从下面的链接下载项目:
翻译自: https://www.journaldev.com/22561/android-mvvm-livedata-data-binding
livedata mvvm