Data BindingライブラリのBinding Methodについて

AndroidのData BindingライブラリのBinding Methodに関する記事がなかなか見つからなかったので、簡単にですがまとめてみます。
Data BindingライブラリやBinding Adapterについては公式ドキュメントなどをご参照ください。

developer.android.com


Binding Methodとは

レイアウトファイルの属性ソースコードのメソッドを紐付ける仕組みのことです。

Data Bindingを使用していると、クリックイベントが発生したときにViewModelのメソッドを呼び出すためにandroid:onClick="@{() -> viewmodel.onClick()}"のような実装を行う機会が多いと思います。

AndroidのData Bindingライブラリでは、レイアウトファイルに記述した属性に対応するメソッドが自動的に呼び出されるようになっています。
具体的には、以下の条件を満たすメソッドが呼び出されます。

  • メソッド名: レイアウトファイルで指定した属性名に対し「set + 属性名」というメソッド名である
  • 引数の型: バインディング式で指定したオブジェクトが代入可能な型である


例えば、Swiperefreshlayoutには以下のメソッドが定義されています。

void setOnRefreshListener(OnRefreshListener listener);
void setRefreshing(boolean refreshing);

また、OnRefreshListenerの定義は以下の通りです。

interface OnRefreshListener {
  void onRefresh();
}


このとき、

  • レイアウトファイルで指定した属性名がonRefreshListener
  • バインディング式で指定したオブジェクトの型がOnRefreshListener型およびその派生型

であればsetOnRefreshListener()を、

  • レイアウトファイルで指定した属性名がrefreshing
  • バインディング式で指定したオブジェクトの型がboolean型(またはBoolean型)

であればsetRefreshing()を自動的に呼び出すことが可能になります。

同様にして、クリックイベントが発生したときにViewModelのメソッドを呼び出すためには、本来であればandroid:onClickListener="@{() -> viewmodel.onClick()}"と記述する必要があります。
これを、android:onClick="@{() -> viewmodel.onClick()}"と記述することでsetOnClickListener()を呼び出すことを可能にしている仕組みがBinding Methodです。


Binding Methodの宣言方法

@BindingMethodsというアノテーションの中にBinding Methodを宣言する形で記述します。
@BindingMethodsは何かしらのクラスに付与する必要があり、AOSPではBinding Adapterを宣言しているクラスに付与されているので*1、基本的にはこちらに倣うのがいいかと思います。

@BindingMethods(
  values = [
    BindingMethod(
      type = View::class, 
      attribute = "android:onClick", 
      method = "setOnClickListener"
    )
  ]
)
object ViewBindingAdapter


この記述によって

  • android:onClickにオブジェクトがセットされると
  • Viewクラスの
  • setOnClickListener()が呼び出される

ようになります。

ただこの場合、引数がView.OnClickListener型のsetOnClick()という関数を定義し、@BindingAdapterアノテーションを付与することでも実現可能です。
関数の定義の仕方はいくつかありますが、object内の関数として定義する場合は@JvmStaticアノテーションを付与する必要があります。

// トップレベル関数として
@BindingAdapter
fun setOnClick(view: View, listener: View.OnClickListener) {
  view.setOnClickListener(listener)
}

// 拡張関数として
@BindingAdapter
fun View.setOnClick(listener: View.OnClickListener) {
  this.setOnClickListener(listener)
}

// object内の関数として
object ViewBindingAdapter {
  @JvmStatic
  @BindingAdapter
  fun View.setOnClick(listener: View.OnClickListener) {
    this.setOnClickListener(listener)
  }
}


以上となります。
ここまで書いておいてあれですが、Binding Methodでやりたいことは基本的にBinding Adapterでも実現可能なため、実際にBinding Methodを使用する機会は少なそうだと感じました。笑