【Android】DataBindingを使ってログインする

## はじめに
[DataBindingを使ってテキストを動的に変更する](https://qiita.com/kilalabu/items/3cded5e7c560de2ed05c)の応用版として
DataBindingを使ってログイン機能を実装してみた。（ログイン画面後のUIは適当なのでコード割愛）

|ログイン成功時|ログイン失敗時|
| ---- | ---- |
|![login.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670405/45a624f8-c616-d3e7-04f2-5d1d33ccb4ed.gif)|![validation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670405/4a4fb2e5-7b48-4780-ad7b-bc0e098a580e.gif)|

## 単方向と双方向バインディングの違いをおさえる
### `@{}` ：単方向バインディングの特徴
* ViewModelの値を変更する -> Viewに自動で反映される
* ユーザーがViewの値を変更する -> ViewModelに反映されない 
* ViewModelにget〇〇〇メソッドを定義する必要がある

### `@={}` ：双方向バインディングの特徴
* ViewModelの値を変更する -> Viewに自動で反映される
* ユーザーがViewの値を変更する -> ViewModelに自動で反映される
* ViewModelにget〇〇〇メソッドとset〇〇〇メソッドを定義する必要がある

##コード

### DataBindingの導入
appのgradleに以下を追加

```java:build.gradle
android {
    dataBinding {
        enabled = true
    }
}
```

### クリックイベントのインターフェースを定義
```java:EventHandlers.java
public interface EventHandlers {
    void onLoginClick(View view);
}
```

### ViewModel

```java:UserViewModel.java
public class UserViewModel extends BaseObservable {
    private String email;
    private String password;
    private String clickText;

    @Bindable public String getEmail() { return email; }

    @Bindable public String getPassword() { return password; }

    @Bindable public String getClickText() { return clickText; }

    // フォームへの入力有無で、ボタン活性・非活性を制御(getEmail()等と同じgetter)
    @Bindable public boolean isButtonEnable() {
        // 入力あり:true  入力なし：false
        return !TextUtils.isEmpty(email) && !TextUtils.isEmpty(password);
    }

    // Emailフォームに値を入力・変更する度に呼び出される
    public void setEmail(String email) {
        this.email = email;
        // この記述でisButtonEnable()が呼ばれる
        notifyPropertyChanged(BR.buttonEnable);
    }

    // Passwordフォームに値を入力・変更する度に呼び出される
    public void setPassword(String password) {
        this.password = password;
        // この記述でisButtonEnable()が呼ばれる
        notifyPropertyChanged(BR.buttonEnable);
    }

    // 入力値のバリデーションチェック
    public boolean isValidInputValue() {
        if (email.length() >= 6 && password.length() >= 6) {
            return true;
        }
        return false;
    }
}
```

### layout

* EditTextはユーザーがViewの値を更新するので必ず双方向バインディングとなる。
* android:enabledでボタンが押せる,押せないを判定できる。

```xml:activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="user"
            type="com.example.loginapp.UserViewModel" />
        <variable
            name="handlers"
            type="com.example.loginapp.EventHandlers" />
    </data>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/clickText"
            android:layout_width="320dp"
            android:layout_height="wrap_content"
            android:text="@{user.clickText}"/>

        <EditText
            android:id="@+id/mail_form"
            android:layout_width="320dp"
            android:layout_height="wrap_content"
            android:imeOptions="actionDone"
            android:inputType="textEmailAddress"
            android:text="@={user.email}"/>

        <EditText
            android:id="@+id/pass_form"
            android:layout_width="320dp"
            android:layout_height="wrap_content"
            android:imeOptions="actionDone"
            android:inputType="textPassword"
            android:text="@={user.password}"/>

        <Button
            android:id="@+id/login"
            android:layout_width="320dp"
            android:layout_height="wrap_content"
            android:text="ログイン"
            android:enabled="@{user.buttonEnable}"
            android:onClick="@{handlers.onLoginClick}"/>

    </LinearLayout>
</layout>
```

### Activity

```java:LoginActivity.java
public class LoginActivity extends AppCompatActivity implements EventHandlers {

    ActivityLoginBinding binding;
    UserViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        // Bindingのインスタンスを取得
        binding = DataBindingUtil.setContentView(this, R.layout.activity_login);

        // xmlのuserにLoginActivityのUserViewModelを紐付ける
        viewModel = new UserViewModel();
        binding.setUser(viewModel);

        // xmlのhandlersにLoginActivityのonLoginClick()を紐付ける
        binding.setHandlers(this);
    }

    // buttonをクリックしたときのイベント処理
    @Override
    public void onLoginClick(View view) {
        if (viewModel.isValidInputValue()) {
            Intent intent = new Intent(LoginActivity.this, MainActivity.class);
            startActivity(intent);
            finish();
        } else {
            binding.clickText.setText("6文字以上入力してください");
        }
    }
}
```