Android DataBinding + Kotlin でつまずいたこと（BindingAdapter多め）

## はじめに
[DataBinding](https://developer.android.com/topic/libraries/data-binding/index.html?hl=ja) を使っていて、つまずいた点とそれを解消した方法を書いていきます。（半分は備忘録）
カスタムセッター（BindingAdapter アノテーション）を作ったときのTips多めです。

## Kotlin で BindingAdapterのエラー
Javaでカスタムセッターを作るとこんな感じで書けました。

```java
@BindingAdapter("imageUrl")
public static void loadImage(ImageView view, String url) {
   // do something
}
```

始めに、Kotlinでカスタムセッターをこう書きました。

```kotlin
class ImageViewBindingAdapter {
    companion object {
        @BindingAdapter("imageUrl")
        @JvmStatic
        fun loadImage(view: ImageView, imageUrl: String) {
            // do something
        }
    }
}
```

> DataBindingAttribute.Companion is not static and requires an object to use, retrieved from the DataBindingComponent.

`companion object` を使って、staticメソッドする方法では、上記のエラーとなってしまう。
下記の方法でカスタムセッターを書くといけます。

```kotlin
object ImageViewBindingAdapter {
    @BindingAdapter("imageUrl")
    @JvmStatic
    fun loadImage(view: ImageView, imageUrl: String) {
        // do something
    }
}
```

Kotlinの拡張関数を使うこともできます。

```kotlin
object ImageViewBindingAdapter {
    @BindingAdapter("imageUrl")
    @JvmStatic
    fun ImageView.loadImage(imageUrl: String) {
        // do something
    }
}
```

XMLは、JavaとKotlinともに同じです。

```xml
<ImageView 
    app:imageUrl="@{viewModel.imageUrl}" />
```

## Kotlin でカスタムセッターに複数のパラメータ指定
Javaだとこんな感じで書けました。

```java
@BindingAdapter({"imageUrl", "error"})
public static void loadImage(ImageView view, String url, Drawable error) {
   // do something
}
```

Kotlinだとこうです。

```kotlin
object ImageViewBindingAdapter {
    @BindingAdapter("imageUrl", "error")
    @JvmStatic
    fun ImageView.loadImage(imageUrl: String, error: Drawable) {
        // do something
    }
}
```

XMLではこう書きます。

```xml
<ImageView 
    app:imageUrl="@{viewModel.imageUrl}"
    app:error="@{@drawable/errorImage}" />
```

以下のように、一部のパラメータしか指定していなかった場合うまく動きません。

```xml
<ImageView 
    app:imageUrl="@{viewModel.imageUrl}" />
```

これは BindingAdapterの `requireAll` 属性がデフォルトで `true` になっているためです。
この場合、すべてのパラメータが指定されていないと呼ばれません。
一部のパラメータでも呼ばれるようにするには、BindingAdapter を以下のように書き換えます。

```kotlin
@BindingAdapter("imageUrl", "error", requireAll = false)
```

## BindingAdapter に Int, Boolean を渡したい
BindingAdapter に Int をセットしたくて、以下のように書いたがうまく動かなかった。

```kotlin
object ImageViewBindingAdapter {
    @BindingAdapter("imageUrl", "position")
    @JvmStatic
    fun ImageView.loadImage(imageUrl: String, position: Int) {
        // do something
    }
}
```

```xml
<ImageView 
    app:imageUrl="@{viewModel.imageUrl}"
    app:position="1" />
```

原因は XML での書き方にあり、以下のように変えると動きます。

```xml
<ImageView 
    app:imageUrl="@{viewModel.imageUrl}"
    app:position="@{1}" />
```

Boolean を渡したい場合はこう書く。

```xml
<ImageView 
    app:imageUrl="@{viewModel.imageUrl}"
    app:dispflag="@{true}" />
```

なぜこう書かないといけないのかわかっていないので、知っている方がいたら教えてください。

## BindingAdapter に リソース ID を渡したい
以下のように、Rクラスを import すればいけます。

```xml
<data>
    <import type="com.sample.R"/>
    <variable 
        name="viewModel"
        type="com.sample.TestViewModel" />
</data>
<ImageView 
    app:src="@{viewModel.resourceId ?: R.drawable.error}" />
```

## android:onClick 以外のイベントを処理したい

まず、標準で用意されている Adapter が存在するか確認しました。
https://android.googlesource.com/platform/frameworks/data-binding/+/master/extensions/baseAdapters/src/main/java/android/databinding/adapters

例えば、 `EditTextView` の `android:onTextChanged` を使いたい場合、[TextViewBindingAdapter.java](https://android.googlesource.com/platform/frameworks/data-binding/+/master/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java) にすでに BindingAdapter があるのでそれを使うことができます。

ここに BindingMethod の記述もあるので、 set**Listener のようなメソッド BindingMethod をわざわざ用意しなくてもよくなるかもしれません。

## 引数に Context を使いたい
DataBinding で、Context を引数に持つメソッドを使いたいとき、以下の記事を参照しました。
`context` とそのまま渡せば動作するんだ。

[DataBindingで引数にcontextを渡すメソッドを扱う](https://qiita.com/konifar/items/dced2076550619ef4ac3)