DataBindingチュートリアル第2回 – アクションのバインディング

前回、データ(User)をレイアウトにバインディングしてその中身(name)を表示する方法を説明しました。 データバインディングを使うとこのようにデータのバインディングだけではなく、イベントのハンドリングもレイアウト側で様々な定義が可能です。 この回では、データバインディングを使ったイベントハンドリングについて説明していきます。

サンプル

今回は前回のサンプルコードに新たに4つのボタンを加えます。この4つのボタンを押した時のアクションのハンドリングについて説明して行きたいと思います。 スクリーンショット

今回のチュートリアルで説明するサンプルコードはこちらからダウンロードできます。

従来の方法

従来の方法では下のようなボタンに対してonClick時に呼び出すメソッドを文字列で指定しておいて

<Button
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:text="SHARE"
    android:onClick="onClickShare"
    />

public void onClickShare(View view)という関数をActivityクラスなどに定義してそこでクリックイベントを処理するか、もしくはJavaコードの方で下のようにOnClickListenerを設定してハンドリングする方法で実装していました。

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {

    }
});

データバインディングを使った方法

データーバインディングを使うと下のような方法でイベントハンドリングの定義をView内に追加できます。今回は4種類の記述方法を説明した4つのボタンを配置しました。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user"
            type="com.goldrushcomputing.databindingbasicsamples.model.User"/>

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

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp">
        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}"/>

        <Button
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:text="SEND"
            android:onClick="@{handler::onClickSend}"
            />

        <Button
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:text="INVITE"
            android:onClick="@{() -> handler.onClickInvite(user)}"
            />

        <Button
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:text="LIKE"
            android:onClick="@{(view) -> handler.onClickLike(user)}"
            />

        <Button
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:text="SHARE"
            android:onClick="@{(view) -> handler.onClickShare(view, user)}"
            />


    </LinearLayout>
</layout>

上のレイアウトのandroid:onClick=の箇所にイベントハンドリングが記述されていますが、全てhandlerというオブジェクトのメソッドを呼び出しているのがわかると思いまます。 このhandlerというオブジェクトにアクセすできるようにレイアウトファイルの上の<data>セクションにuserに加えて、新たにhandlerという変数を定義します。この変数のクラスはMainActivityにします。 これで、handlerを通じてMainActivityに書いたメソッドをレイアウト側から呼び出せるようになります。

MainActivityのonCreate()で下のように自分自身をhandler変数に登録します。

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User("Benjamin");
binding.setUser(user);

binding.setHandler(this);

これで準備は整いました。 ここからMainActivityにイベントを渡していくのですが、大きく分けてメソッド参照とリスナーバインディングという2つの方法があります。 Googleの公式のチュートリアルではリスナーバインディングの記述方法として3つ違うものが出てきて私自身初めて見たときに混乱したので、今回この3つの方法を分けて説明しようと思いました。 ですので、今回はメソッド参照の例を1つ、リスナーバインディングの例を3つ説明します。

各記述方法に共通しているのは、データをバインドしたときと同じように@{}でくくることです。

メソッド参照

まずメソッド参照ですが、handler::onClickSendのようにオブジェクト名 + コロン2つ + メソッド名で記述します。

<Button
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:text="SEND"
    android:onClick="@{handler::onClickSend}"
    />

これでhandlerに同じ名前の関数をViewクラスのオブジェクトをパラメーターとして記述すればイベントを受け取ることができます。

public void onClickSend(View view){
     String msg = "Sending...";
     Toast.makeText(this, msg,
            Toast.LENGTH_SHORT).show();
}

リスナーバインディング1

次にラムダ式を使ったバインディングの方法を説明します。この方法はリスナーバインディングと呼ばれています。 リスナーバインディングの良いところはメソッドにView以外のデータを渡すことができる点です。上のメソッド参照では、クリックの起こったViewオブジェクトしか取得できませんでしたがリスナーバインディングを使うと 下のようにこのレイアウトに現在バインドされたUserオブジェクトをメソッドに私ことができます。

<Button
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:text="INVITE"
    android:onClick="@{() -> handler.onClickInvite(user)}"
    />

上のように記述すると、下のような型のメソッドで受け取れます。

public void onClickInvite(User user){
    String msg = "Inviting " + user.getName() + "...";
    Toast.makeText(this, msg,
            Toast.LENGTH_SHORT).show();
}

リスナーバインディング2

上で説明した記述方法はGoogleのドキュメントによると下のラムダ式の省略形ということだそうです。()の箇所に(view)のようにviewというパラメータを記述するようです。

<Button
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:text="LIKE"
    android:onClick="@{(view) -> handler.onClickLike(user)}"
    />
public void onClickLike(User user){
    String msg = "Liking " + user.getName() + "'s post...";
    Toast.makeText(this, msg,
            Toast.LENGTH_SHORT).show();
}

リスナーバインディング3

上の2つの例ではuserのみをパラメーターで渡していますが、メソッド参照の時のようにViewオブジェクトも渡したい場合は、下のように記述します。(View view, User user)のように2つのパラメータを渡します。(最初のカッコの中にviewと書くことも必要ですので忘れずに。)

<Button
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:text="SHARE"
    android:onClick="@{(view) -> handler.onClickShare(view, user)}"
    />

こうすると下のようにviewとuser、2つのオブジェクトが渡せます。

public void onClickShare(View view, User user){
    String msg = "Sharing to " + user.getName() + "...";
    Toast.makeText(this, msg,
            Toast.LENGTH_SHORT).show();
}

この方法ですと、handler.onClickShare(view, user, message)のように何個でもハンドラーにパラメーターを渡すことができます。 3つのリスナーバインディングの方法を説明しましたが、2番目のものは1番目が省略形となっていることの説明のようなものですので、実際には1番目と3番目を多用することになるかと思います。1番目はメソッド参照でも同じことができるので実際には3番目の型だけ体に染み込ませて置けば十分だと思います。

メソッド参照・リスナーバインディングのメリット

メソッド参照およびリスナーバインディングを使うメリットは、コンパイラー時に対応するメソッドがhandlerオブジェクト(この場合MainActivity)の中にあるかどうかをコンバイラーがチェックし、なければエラーを返してくれるところです。 従来のandroid:onClick="onClickShare"という記述だとコンパイラーがチェックしないので、タイプミスやコード変更で対応するメソッドがない時に実行時にクラッシュするまで問題に気づかないことがよくあります。

今回はデータバインディングを使ったイベントハンドリングの方法を説明しました。リスナーバインディングの記述は少し特殊に感じるかもしれませんが、慣れるととても使いやすいので積極的に使って見ることをおすすめします。

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

NEXT

DataBindingチュートリアル第3回 – BindingAdapter(バインディングアダプター)