DataBindingチュートリアル第5回 – データに表現させる

この回ではこれまでの回以上にデータに自分自身を表現させるようなコーディングのテクニックを説明して行きたいと思います。 サンプルコードは第1回〜第4回で使ってきたものを引き継いで使います。

Visibilityの操作

この回では下のように、ユーザーが自分の友達かどうかによって表示するボタンを切り替えるという効果をDataBindingを使って実装する方法を説明します。

スクリーンショット

通常ですとCheckBoxからアクションを受け取って、Javaコード側で各ボタンの表示/非表示を切り替えるコードを書かないといけないのですがDataBinding を使うと下のようにButtonandroid:visibilityタグだけでこの切り替えができます。

<Button
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:text="INVITE"
    android:onClick="@{() -> handler.onClickInvite(user)}"
    bind:font="@{`Roboto-Bold.ttf`}"
    android:visibility="@{user.isFriend ? View.GONE : View.VISIBLE}"

上のButtonタグ内では、Userが自分の友達であるか(isFriendフラグがtrueか)によってvisibilityの設定を操作しています。 これを実現するために、

View.GONEView.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:bind="http://schemas.android.com/apk/res-auto"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="user"
            type="com.goldrushcomputing.databindingbasicsamples.model.User"/>

        <variable
            name="handler"
            type="com.goldrushcomputing.databindingbasicsamples.MainActivity"/>

        <import type="android.view.View"/>
    </data>

<import type="android.view.View"/>という宣言を<data>セクションに追加するだけで、Viewクラスの変数や関数をレイアウト内で使えるようになります。 自分でBindingオブジェクトに設定したデータだけではなく、このように既存のAndroidのクラスをimportしてレイアウト内で使えます。 View以外のクラスではDateクラスを日時に関連したものを表示する時によく使います。

次にUserクラスにisFriendを追加します。

public class User {
    String firstName;
    String lastName;
    public final ObservableField<Boolean> isFriend = new ObservableField<>();
    String profileImageUrl;

通常ですと boolean isFriend = false という記述になりますが、ここではObservableFieldというものを使います。 ObservableFieldはこちらの公式ドキュメントの”オブザーバブル フィールド”というセクションに出ていますが、 ObservableFieldを使うとフィールド(メンバー変数のこと)の値が変更された時にこのフィールドをバインドしているViewが自動的にリロードされます。
この場合では、isFriendが変更されると、このフィールドにバインドされたView(このフィールドにアクセスしてなんらかの@{}表現を書いているビュー)の@{}部分が再評価されるので、isFrinedをtrueにしたり、falseにしたりするとButtonの表示/非表示が連動して起きるようになります。 isFriendフラグ<–>Buttonの間になんのコードも挟まなくて良いのでコードが非常にスッキリします。
ここまでの変更で、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フラグをトグルします。

public void onCheckFriend(User user){
    user.isFriend.set(user.isFriend.get() ? false : true);
}

ここで1つ注意しないといけないのは、isFriendObservableField<Boolean>クラスなので、get()という関数を使って中身のboolean型のデータを取り出さないといけないところです。xmlファイルでisFriendを参照するときはget()は必要なく、android:checked="@{user.isFriend}"などのようにそのままアクセスできます。(ちなみにObservableFieldに値をセットするときはset()関数を使います。)

これで重要なポイントは全て実装が終わりました。あとはモデルクラスを少しだけ修正しますので、もう少しお付き合いください。

コンストラクタの変更

UserクラスにisFriendというメンバーが変わったのと、nameをfirstNameとlastNameに分けたので、コンストラクタを下のように変更しました。

public User(String firstName, String lastName, boolean isFriend, String profileImageUrl) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.isFriend.set(isFriend);
    this.profileImageUrl = profileImageUrl;
}

またMainActivityのonCreateではこの変更に合わせて下のようにUserオブジェクトを初期化するように変更しました。

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

    String firstName = "Benjamin";
    String lastName = "Franklin";
    String profileImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/c/cc/BenFranklinDuplessis.jpg/1280px-BenFranklinDuplessis.jpg";

    User user = new User(firstName, lastName, false, profileImageUrl);
    binding.setUser(user);

    binding.setHandler(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"
    bind: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}"

データの属性によって見た目を変えたい時にこのようなパターンが多く使えます。データの属性によって見た目を変えるというコードはJavaにかくとコントローラの本質的な仕事と違うこともありコントローラーのコードが読みにくくなってしまうのでDataBindingを使ってレイアウト側にロジックを移せると非常にコードが読みやすくなります。
またこの部分は考えていても書いていても非常に楽しい部分ですので是非トライしてみてください。

サンプル

サンプルプロジェクトはこちらからダウンロードできます。


まとめ

今回はユーザーの属性が変わるとそれに応じてユーザーインターフェースが動的に変わるというデモを作ってみました。

などなどこのパターンを使ってDataBindingの恩恵を受けられる機会は非常に多いのではないでしょうか。

チュートリアルの内容を自分のプロジェクトに反映させる時にわからないことや問題にぶつかった時のために、Fourhandsではオンラインのメンタリングサービスを提供しています。
ビデオチャットやスクリーン共有をつかて実際にコードをメンターに見てもらいながら問題の解決や、考え方のアドバイスがもらえるサービスですので、是非活用して見てください。
fourhands.com

NEXT

DataBindingチュートリアル第6回 – データに表現させる