見出し画像

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

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

サンプル

画像1

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

従来の方法

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

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

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

findViewById<Button>(R.id.sendButton).setOnClickListener { view ->
                       
}

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

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

<?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" />
   </data>

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical">

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.name}" />

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

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

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

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

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

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

binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
val user = User("Benjamin")
binding.user = user
binding.handler = 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)}"
   />

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

fun onClickInvite(user: User) {
   val msg = "Inviting ${user.name} ..."
   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)}"
   />
fun onClickLike(user: User) {
   val msg = "Liking ${user.name}'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つのオブジェクトが渡せます。

fun onClickShare(view: View, user: User) {
   val msg = "Sharing to ${user.name} ..."
   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"という記述だとコンパイラーがチェックしないので、タイプミスやコード変更で対応するメソッドがない時に実行時にクラッシュするまで問題に気づかないことがよくあります。

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

GitHub

DataBindingTutorialのコードはこちらのGitHubでも公開しています。

今回のサンプルはコミットハッシュ

ef6b3538427e017c54badd70a487b4201cb484b9

で保存してあります。



この記事が気に入ったらサポートをしてみませんか?