DataBindingを試す
DataBindingがアツいらしいと聞いて試してみました。簡単な使い方をするなら想像以上に簡単でした。
今までActivityなどでfindViewByIdを書きたくないから、ButterKnifeをどのプロジェクトでも使っていたのですが、DataBindingを使えば同じようなことができます。
両者を使ってみて感じたのは、ButterKnifeがレイアウトXMLをJavaコードに持ってくるイメージであるとすれば、DataBindingはJavaコードをレイアウトXMLに持っていくイメージであるということです。
DataBindingを使うことで、Javaで作成したコードを、レイアウトXMLに埋め込むことができるようになります。レイアウトXMLでどのデータを使うか指定しておけば、Activityで「このクラス(のインスタンス)を使ってくれ」と指定するだけでその内容を表示できたりします。
具体的な使い方はData Binding Guide – Android Developersを参照してください。
Android Studio 1.3以上であることが必須です。
Android Gradle Plugin 1.5.0-alpha1以上を使っていることが必須、でした。
Android Studio 2.0 betaになると、コード補完のサポートがより強力になってます。
build.gradleでDataBindingの設定を有効にすることで利用できます。
android {
....
dataBinding {
enabled = true
}
}
public class Character{
public String name;
public int age;
public String skill;
public Character(String name, int age, String skill){
this.name = name;
this.age = age;
this.skill = skill;
}
}
DataBindingを使ってアクセスするには、publicなフィールドであるか、privateなフィールドである場合publicなgetterがあることが必須です。
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
>
<data>
<variable
name="chara"
type="jp.gcreate.sample.databinding.Character"
/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="jp.gcreate.sample.databinding.MainActivity"
>
<TextView
android:id="@+id/chara_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{chara.name}"
/>
<TextView
android:id="@+id/chara_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{Integer.toString(chara.age)}"
/>
<TextView
android:id="@+id/chara_skill"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{chara.skill}"
/>
</LinearLayout>
</layout>
ポイントはこんな感じ。
- レイアウトXMLを`
`タグで囲む - レイアウトXML内で利用するクラスを``タグ内で宣言する
- ``タグ内で宣言するクラス名は完全修飾ドメイン名
- `
`タグ内で宣言した`name`を使ってアクセスする - Javaのコードを埋め込める(`Integer.toString()`とか)
- コードは`@{}`で囲む
ちなみにandroid:text
のところに埋め込むデータは、TextView.setText()
を使って設定されるので、ここにint型のデータを埋め込むときは注意が必要です。何も考えずにint型のデータを表示しようとすると、そのint値がリソースIDとして認識されてしまいエラーになるからです。その際のエラー内容も、「そんなリソースIDないぞ」という内容で軽くハマりました。
ちなみにDataBindingによって生成されるクラス名は、レイアウトXMLのファイル名をスネークケースからキャメルケースに変換したものに、Bindingを付け加えたものになります。
activity_main.xmlならActivityMainBindingに、hoge_hoge.xmlならHogeHogeBindingになります。
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
Character chara;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
chara = new Character("桃太郎", 18, "きびだんご");
binding.setChara(chara);
}
}
レイアウトXMLに紐付けるクラスを、setChara()
で渡すことで、渡したCharacterクラスのデータが表示されます。ちなみにレイアウトXMLで<variables name="chara" .../>
と設定しているからsetChara()
になっています。nameをhogeにしていたらsetHoge()
になります。
単に表示するだけでは「何が便利なのか」という感じるかもしれません。
例えば桃太郎のスキルを「鬼退治」に変更したいとします。findViewByIdを使わずとも、DataBindingを使えばレイアウトXMLでidを割り振ったViewにアクセスすることができます。
@Override
public boolean onTouchEvent(MotionEvent event) {
binding.charaSkill.setText("鬼退治");
return super.onTouchEvent(event);
}
これを追加すると、タッチイベントが発生したらスキルの内容が「鬼退治」に変わります。
レイアウトXMLと紐付けるクラスを以下のように書き換えると、インスタンスの内容が変更されるだけでUIに表示されるデータも変わります。
public class Character extends BaseObservable {
public String name;
@Bindable
public int age;
public String skill;
public Character(String name, int age, String skill) {
this.name = name;
this.age = age;
this.skill = skill;
}
public void countUp(){
age++;
notifyPropertyChanged(jp.gcreate.sample.databinding.BR.age);
}
}
- BaseObservableのサブクラスにする
- 自動的に変更させたいpublicなフィールド、もしくはgetterに@Bindableアノテーションを付ける
- データの変更が生じるメソッドで`notifyPropertyChanged()`を呼ぶ
Activityのコードを以下のように書き換えてみます。
@Override
public boolean onTouchEvent(MotionEvent event) {
chara.countUp();
return super.onTouchEvent(event);
}
こうするとタッチイベントが生じる度に桃太郎が年をとるようになります。いちいち自分でTextViewに変更済みのデータを設定する必要がなくなります。
今のところ私はこの程度しか使っていないのですが、これだけでも充分便利だなぁと思います。とりあえずは簡単なところから試してみてはいかがでしょうか。