この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Databindingはサポートライブラリで提供されているバインディングライブラリです XML上でviewとデータを関連づけたり、データ変更に伴うUI動作などのプロセスを自動化させることができ、単純にfindviewIdやButterKnifeの一部の機能の替わりとしても利用が可能です 他にも様々な便利機能が用意されてますが、導入からモデル-view間の通知までのサンプルを紹介してみます
導入
DataBindingの使用にはgradleプラグイン1.5以上が必要になります
gladle
android {
....
dataBinding {
enabled = true
}
}
もしくは
project/build.gradle
dependencies {
....
classpath "com.android.databinding:dataBinder:1.0-rc4"
}
app/build.gradle
apply plugin: 'com.android.databinding'
基本実装
モデルクラスを準備
public class Login {
private String email;
private String password;
public Login(String email, String password) {
this.email = email;
this.password = password;
}
/* getter and setter */
}
XMLに記述
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable name="login" type="jp.sample.model.Login"/>
</data>
<LinearLayout
<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
android:singleLine="true"
android:text="@{login.email}"/>
...
</LinearLayout>
</layout>
- ルートを< layout >にする
- < data >は< layout >内での変数のラッパーとしての役割
- @{ }構文でプロパティが設定される
- クラスがnullの場合、パラメータにnullや0が設定される
データをバインド
MainActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setLogin(new Login("email","password"));
}
- レイアウトファイル名に基づいてBindingクラスが生成される
- set***メソッドの名前は< variable >のnameフィールドに依存する
ここまでがDataBindingを利用する上での基本的な部分になります
モデルの変更をviewに反映させる
ObservableFieldを使う
private static class Login {
public final ObservableField email =
new ObservableField<>();
public final ObservableField password =
new ObservableField<>();
}
Login login = new Login()
login.email.set("hoge")
<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
android:singleLine="true"
android:Text="@{login.email}"/> /* ObservableField */
...
- モデルの値が変わるとUI(text等)も変わる
- フィールドの型がObservableFieldになってしまう
- プリミティブ型用にはObservableBooleanとかが用意されてる
BaseObservableを継承してつかう
public class Login extends BaseObservable {
private String email;
private String password;
public Login(String email, String password) {
this.email = email;
this.password = password;
}
@Bindable
public String getEmail() {
return email;
}
@Bindable
public String getPassword() {
return password;
}
public void setEmail(String email) {
this.email = email;
notifyPropertyChanged(BR.email);
}
public void setPassword(String password) {
this.password = password;
notifyPropertyChanged(BR.password);
}
}
Login login = new Login()
login.setEmail("hoge")
<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
android:singleLine="true"
android:Text="@{login.email}"/>
...
- 通知したいパラメータに@Bindableをつける
- @BindableがついてるプロパティがBRクラスのフィールドに追加される
- notifyPropertyChanged(int fieldId)でviewへの通知を行う
- extendsを消費してしまう(したくない場合はObservableを実装する)
以上、上記のObservableFieldを利用したパターンとBaseObsaervableを継承したパターンがモデルの変更をviewに通知する例になります
モデルとViewの双方向で通知する
public class LoginViewModel extends BaseObservable {
private String email;
private String password;
private boolean isEditMode = false;
public LoginViewModel(String email, String password) {
this.email = email;
this.password = password;
}
public SimpleTextWatcher emailWatcher = new SimpleTextWatcher() {
@Override
public void onTextChanged(String value) {
isEditMode = true;
setEmail(value);
isEditMode = false;
}
};
public SimpleTextWatcher passwordWatcher = new SimpleTextWatcher() {
@Override
public void onTextChanged(String value) {
isEditMode = true;
setPassword(value);
isEditMode = false;
}
};
@Bindable
public String getEmail() {
return email;
}
@Bindable
public String getPassword() {
return password;
}
public void setEmail(String email) {
this.email = email;
if (!isEditMode) {
notifyPropertyChanged(BR.email);
}
}
public void setPassword(String password) {
this.password = password;
if (!isEditMode) {
notifyPropertyChanged(BR.password);
}
}
}
- 双方向で通知がループしないようにフラグを設定
- サンプルではTextWatcherのカスタムクラスを作成してます
public abstract class SimpleTextWatcher implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
onTextChanged(s.toString());
}
public abstract void onTextChanged(String value);
}
カスタムしたTextWatcherをEditTextにバインドするためにaddTextChangeListenerを設定する
<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
android:singleLine="true"
android:text="@{viewModel.email}"
app:addTextChangedListener="@{viewModel.emailWatcher}"/>
...
これで画面上でEditTextに入力した値をモデルに適用させる事ができました
- DataBindingの機能によって、標準でviewコンポーネントごとにいくつかのリスナー等がxmlで設定できるように用意されます
- 上記はBaseObservableを継承した例ですが、ObservableFieldを利用したパターンでもviewからの通知は可能です。その場合はObservableのプロパティが変更された時のコールバックメソッドがあるので、それを使うと良いでしょう
まとめ
今まではモデルの変更ロジックに応じて、UIの動作を記述していましたがDataBindingの利用でActivityやFragmentがよりスッキリさせる事できそうですね