このチュートリアルでは、AndroidアプリケーションでAndroid MVVMアーキテクチャパターンを検討して実装します。以前に、Android MVPパターンについて説明しました。
なぜこれらのパターンが必要なのですか?すべてを1つのアクティビティやフラグメントに追加すると、テストやコードのリファクタリングで問題が発生します。したがって、コードの分離とクリーンなアーキテクチャの使用が推奨されます。
Android MVVM
MVVMは、モデル、ビュー、ビューモデルの頭字語です。
- モデル:アプリケーションのデータを保持します。ビューと直接通信できません。一般的には、データをViewModelにObservables経由で公開することが推奨されます。
- ビュー:アプリケーションのUIを表しますが、アプリケーションロジックはありません。ViewModelを監視します。
- ViewModel:モデルとビューの間のリンクとして機能します。モデルからデータを変換する責任があります。ビューにデータストリームを提供します。また、ビューを更新するためのフックやコールバックを使用します。モデルからデータを要求します。
以下のフローは、コアMVVMパターンを示しています。 これはMVPとどう違いますか?
- ViewModelは、中間層でのPresenterを置き換えます。
- PresenterはViewへの参照を保持しますが、ViewModelはしません。
- Presenterは、クラシックな方法(メソッドのトリガー)を使用してViewを更新します。
- ViewModelはデータストリームを送信します。
- PresenterとViewは1対1の関係にあります。
- ViewとViewModelは1対多の関係にあります。
- ViewModelは、Viewが自分を聞いていることを知りません。
AndroidでMVVMを実装する方法は2つあります:
- Data Binding
- RXJava
このチュートリアルでは、Data Bindingのみを使用します。 Data Bindingライブラリは、xmlレイアウトでデータを直接バインドするためにGoogleによって導入されました。 Data Bindingに関する詳細については、このチュートリアルを参照してください。ユーザーの入力を求めるシンプルなログインページの例を作成します。 ViewModelがViewの参照を保持せずにToastメッセージを表示するタイミングをViewに通知する方法を見てみましょう。
参照を持たないクラスに通知する方法は可能ですか? これは3つの異なる方法で行うことができます:
- 双方向データバインディングを使用する
- LiveDataを使用する
- RxJavaを使用する
Two Way Data Binding
Two-way Data Bindingは、オブジェクトをXMLレイアウトにバインドする技術であり、オブジェクトとレイアウトの間で双方向にデータを送信できます。私たちの場合、ViewModelはレイアウトにデータを送信し、変更を観察することもできます。そのためには、XMLで定義されたBindingAdapter
とカスタム属性が必要です。Binding Adapterは属性の変更を監視します。以下の例を通じてTwo-way Data Bindingについて詳しく学びましょう。
Android MVVMの例プロジェクト構造
データバインディングライブラリの追加
以下のコードをアプリのbuild.gradleファイルに追加します:
android {
dataBinding {
enabled = true
}
}
これにより、アプリケーションでデータバインディングが有効になります。
依存関係の追加
次の依存関係をbuild.gradle
ファイルに追加してください:
implementation 'android.arch.lifecycle:extensions:1.1.0'
Model
モデルはユーザーのメールアドレスとパスワードを保持します。次のUser.javaクラスがそれを行います:
package com.journaldev.androidmvvmbasics.model;
public class User {
private String email;
private String password;
public User(String email, String password) {
this.email = email;
this.password = password;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
}
双方向データバインディングを使用すると、オブジェクトをXMLレイアウトにバインドできるため、オブジェクトはレイアウトにデータを送信し、その逆も可能です。双方向データバインディングの構文は@={variable}
です。
Layout
activity_main.xmlのコードは以下の通りです:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:bind="https://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.journaldev.androidmvvmbasics.viewmodels.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">
<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="@={viewModel.userEmail}" />
<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="@={viewModel.userPassword}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:onClick="@{()-> viewModel.onLoginClicked()}"
android:text="LOGIN"
bind:toastMessage="@{viewModel.toastMessage}" />
</LinearLayout>
</ScrollView>
</layout>
データバインディングを使用するには、レイアウトタグを先頭に設定する必要があります。ここで、ViewModelがデータをViewにバインドします。()-> viewModel.onLoginClicked()
は、ViewModelで定義されたボタンクリックリスナーラムダを呼び出します。EditTextはモデル(via ViewModel)の値を更新します。bind:toastMessage="@{viewModel.toastMessage}"
は、双方向データバインディングのために作成したカスタム属性です。ViewModelのtoastMessageの変更に基づいて、BindingAdapterがViewでトリガーされます。
ViewModel
LoginViewModel.javaのコードは以下の通りです:
package com.journaldev.androidmvvmbasics.viewmodels;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.text.TextUtils;
import android.util.Patterns;
import com.android.databinding.library.baseAdapters.BR;
import com.journaldev.androidmvvmbasics.model.User;
public class LoginViewModel extends BaseObservable {
private User user;
private String successMessage = "Login was successful";
private String errorMessage = "Email or Password not valid";
@Bindable
private String toastMessage = null;
public String getToastMessage() {
return toastMessage;
}
private void setToastMessage(String toastMessage) {
this.toastMessage = toastMessage;
notifyPropertyChanged(BR.toastMessage);
}
public void setUserEmail(String email) {
user.setEmail(email);
notifyPropertyChanged(BR.userEmail);
}
@Bindable
public String getUserEmail() {
return user.getEmail();
}
@Bindable
public String getUserPassword() {
return user.getPassword();
}
public void setUserPassword(String password) {
user.setPassword(password);
notifyPropertyChanged(BR.userPassword);
}
public LoginViewModel() {
user = new User("","");
}
public void onLoginClicked() {
if (isInputDataValid())
setToastMessage(successMessage);
else
setToastMessage(errorMessage);
}
public boolean isInputDataValid() {
return !TextUtils.isEmpty(getUserEmail()) && Patterns.EMAIL_ADDRESS.matcher(getUserEmail()).matches() && getUserPassword().length() > 5;
}
}
レイアウトで呼び出されたメソッドは、上記のコードで同じシグネチャで実装されています。メソッドの XML 対応部分が存在しない場合は、属性をapp:
に変更する必要があります。上記のクラスは、ViewModelを拡張することもできます。しかし、toastMessage
プロパティが変更されたときにデータをストリームに変換して通知するために、BaseObservableが必要です。XML で定義された toastMessage カスタム属性の getter と setter を定義する必要があります。setter 内では、データが変更されたことを観測者(アプリケーション内の View になります)に通知します。View(私たちのアクティビティ)は適切なアクションを定義できます。
BR クラスは、プロジェクトを再構築するとデータバインディングから自動生成されます
「MainActivity.java」クラスのコードは以下の通りです:
package com.journaldev.androidmvvmbasics.views;
import android.databinding.BindingAdapter;
import android.databinding.DataBindingUtil;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.journaldev.androidmvvmbasics.R;
import com.journaldev.androidmvvmbasics.databinding.ActivityMainBinding;
import com.journaldev.androidmvvmbasics.viewmodels.LoginViewModel;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
activityMainBinding.setViewModel(new LoginViewModel());
activityMainBinding.executePendingBindings();
}
@BindingAdapter({"toastMessage"})
public static void runMe(View view, String message) {
if (message != null)
Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show();
}
}
データバインディングのおかげで、ActivityMainBinding
クラスがレイアウトから自動生成されます。toastMessage 属性がボタンで定義された場合に、@BindingAdapter
メソッドがトリガーされます。XML および ViewModel で定義された同じ属性を使用する必要があります。したがって、上記のアプリケーションでは、ViewModel が View の変更を監視してモデルを更新します。また、モデルは ViewModel を介してビューを更新できます。notifyPropertyChanged
を使用します。上記のアプリケーションの動作の出力は以下の通りです:これで、Android MVVM Using DataBinding のチュートリアルは終了です。以下のリンクからプロジェクトをダウンロードできます。
Source:
https://www.digitalocean.com/community/tutorials/android-mvvm-design-pattern