DataBindingチュートリアル第5回 – データに表現させる
この回ではこれまでの回以上にデータに自分自身を表現させるようなコーディングのテクニックを説明して行きたいと思います。 サンプルコードは第1回〜第4回で使ってきたものを引き継いで使います。
Visibilityの操作
この回では下のように、ユーザーが自分の友達かどうかによって表示するボタンを切り替えるという効果をDataBindingを使って実装する方法を説明します。
通常ですとCheckBoxからアクションを受け取って、Kotlinコード側で各ボタンの表示/非表示を切り替えるコードを書かないといけないのですがDataBinding を使うと下のようにButtonのandroid:visibilityタグだけでこの切り替えができます。
サンプルプロジェクトはこちら↓↓からダウンロードできます!
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="INVITE"
android:onClick="@{() -> handler.onClickInvite(user)}"
app:font="@{`Roboto-Bold.ttf`}"
android:visibility="@{user.isFriend ? View.GONE : View.VISIBLE}"
/>
上のButtonタグ内では、Userが自分の友達であるか(isFriendフラグがtrueか)によってvisibilityの設定を操作しています。 これを実現するために、
- Viewクラスをlayoutファイルにimportする
- UserクラスにisFriendというboolean型のメンバーを追加する
という変更を加えます。
View.GONEやView.VISIBLEというViewクラスの変数を使うために、レイアウトファイル(activity_main.xml)にViewクラスをimportする必要があります。
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".MainActivity">
<data>
<variable
name="user"
type="com.goldrushcomputing.databindingtutorial.model.User" />
<variable
name="handler"
type="com.goldrushcomputing.databindingtutorial.MainActivity" />
<import type="android.view.View"/>
</data>
<import type="android.view.View"/>という宣言を<data>セクションに追加するだけで、Viewクラスの変数や関数をレイアウト内で使えるようになります。 自分でBindingオブジェクトに設定したデータだけではなく、このように既存のAndroidのクラスをimportしてレイアウト内で使えます。 View以外のクラスではDateクラスを日時に関連したものを表示する時によく使います。
次にUserクラスにisFriendを追加します。
data class User(
var firstName: String,
var lastName: String,
var profileImageUrl: String,
var isFriend: ObservableBoolean
)
通常ですとvar isFriend: Boolean という記述になりますが、ここではObservableBooleanというものを使います。
ObservableBooleanはObservableFieldのなかまです。
ObservableFieldを使うとフィールド(メンバー変数のこと)の値が変更された時にこのフィールドをバインドしているViewが自動的にリロードされます。
この場合では、isFriendが変更されると、このフィールドにバインドされたView(このフィールドにアクセスしてなんらかの@{}表現を書いているビュー)の@{}部分が再評価されるので、isFrinedをtrueにしたり、falseにしたりするとButtonの表示/非表示が連動して起きるようになります。 isFriendフラグ<-->Buttonの間になんのコードも挟まなくて良いのでコードが非常にスッキリします。
ここまでの変更で、isFriendフラグを変えるたびにボタンの表示、非表示が切り替わるようになりました。
ObservableFieldは、型を指定して、ObservableField<User>とか、ObservableField<Follower>などのように宣言しますが、Int、Boolean、Stringなどのプリマティブな型に対しては、ObservableInt, ObservableBoolean, ObservableStringなどが別途用意されています。今回はisFriendというBooleanフラグをObservableにした(観察可能にした)いので、ObservableBooleanでisFriendを定義しました。
isFriendフラグを切り替えて本当に表示されるボタンが変わるかどうかを確かめるためにisFriendフラグをコントロールするUIを追加します。
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Is my friend"
android:checked="@{user.isFriend ? true : false}"
android:onClick="@{() -> handler.onCheckFriend(user)}" />
このようなチェックボックスを足してisFriendを切り替えるようにします。 まずandroid:checked="@{user.isFriend ? true : false}"の部分で、isFriendフラグによってチェックボックスのチェックの状態を自動で切り替えるようにします。 次にandroid:onClick="@{() -> handler.onCheckFriend(user)}でonClickアクションをhandlerのonCheckFriendにバインドします(第2回で説明したリスナーバインドです)。
handlerであるMainActivityでこのアクションを受け取り、下のようにuserのisFriendフラグをトグルします。
fun onCheckFriend(user: User) {
//isFriendの値をトグルする
user.isFriend.set(!user.isFriend.get())
}
ここで1つ注意しないといけないのは、isFriendはObservableField<Boolean>クラスなので、get()という関数を使って中身のboolean型のデータを取り出さないといけないところです。
xmlファイルでisFriendを参照するときはget()は必要なく、android:checked="@{user.isFriend}"などのようにそのままアクセスできます。
また、ObservableBooleanに値をセットするときはset()関数を使います。
これで重要なポイントは全て実装が終わりました。あとはモデルクラスを少しだけ修正しますので、もう少しお付き合いください。
コンストラクタの変更
UserクラスにisFriendというメンバーが変わったのと、nameをfirstNameとlastNameに分けたので、コンストラクタを下のように変更しました。
data class User(
var firstName: String,
var lastName: String,
var profileImageUrl: String,
var isFriend: ObservableBoolean
)
またMainActivityのonCreateではこの変更に合わせて下のようにUserオブジェクトを初期化するように変更しました。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
val firstName = "Benjamin"
val lastName = "Franklin"
val profileImageUrl =
"https://upload.wikimedia.org/wikipedia/commons/2/25/Benjamin_Franklin_by_Joseph_Duplessis_1778.jpg"
val user = User(firstName, lastName, profileImageUrl, ObservableBoolean(false))
binding.user = user
binding.handler = this
}
これで、下のようにCheckBoxをクリックしてBenjamin Franklinを友達にすると、SEND、 LIKE、SHAREボタンが表示されます。チェックを外すとINVITEボタンが再度表示されるようになります。
リソースと組み合わせて使う
DataBindingの@{}タグの中では@string、@drawable、@dimenと行ったリソースにもアクセスできます。 これらを使うとデータによってUIを効果的に変化させることができますので、この機会を使ってもう少しサンプルコードを進化させたいと思います。
下のコードはサンプルコードの中でユーザーの名前を表示しているTextViewの記述です。
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/nameFormat(user.firstName, user.lastName)}"
android:textSize="20sp"
app:font="@{`AppleGaramond-BoldItalic.ttf`}"
android:textColor="@{user.isFriend ? @color/colorFriend : @color/colorStranger}"
/>
nameFormatという下のように定義した文字列を読み込んで、そこにfirstNameとlastNameを入れて表示しています。この場合はfirstNameとlastNameの間にスペースが入って表示されます。
<string name="nameFormat">%1$s %2$s</string>
またテキストのカラーを友達かそうでないかを動的に切り替えています。
android:textColor="@{user.isFriend ? @color/colorFriend : @color/colorStranger}"
データの属性によって見た目を変えたい時にこのようなパターンが多く使えます。データの属性によって見た目を変えるというコードはKotin側に書くとコントローラの本質的な仕事と違うこともありコントローラーのコードが読みにくくなってしまうのでDataBindingを使ってレイアウト側にロジックを移せると非常にコードが読みやすくなります。
またこの部分は考えていても書いていても非常に楽しい部分ですので是非トライしてみてください。
まとめ
今回はユーザーの属性が変わるとそれに応じてユーザーインターフェースが動的に変わるというデモを作ってみました。
フォロワーにだけダイレクトメッセージボタンを表示する。
営業時間内かどうかによってお店に電話するボタンを表示/非表示にする
古い投稿かどうか、未読か既読かによって背景の色を変える。
などなどこのパターンを使ってDataBindingの恩恵を受けられる機会は非常に多いのではないでしょうか。
GitHub
DataBindingTutorialのコードはこちらのGitHubでも公開しています。
https://github.com/mizutori/AndroidDataBindingTutorial
今回のサンプルはコミットハッシュ
2b65f2c3d9c72d2c1bce75037842bd41575bbec3
で保存してあります。
git clone git@github.com:mizutori/AndroidDataBindingTutorial.git
git checkout 2b65f2c3d9c72d2c1bce75037842bd41575bbec3