【Android】DataBindingを用いたCustomView作成

# 概要
カスタムビューを作成した際、複数リスナー用DataBindingを行う際に手間取ったのでメモ。


# 説明用サンプル
カスタムビュー内に2個ボタンを配置し、ボタン押下時のリスナーをViewModelでセットする処理を作成。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694762/2f49399d-80c7-b95e-149e-2465aca38ef2.png" width="30%">

まず、カスタムビュー用のxmlを作成。ボタンを2つ配置。
```view_custom.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:app="http://schemas.android.com/apk/res-auto"
	android:layout_width="match_parent"
	android:layout_height="match_parent">

	<Button
		android:id="@+id/button1"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:text="ボタン1"
		android:textSize="20sp"
		app:layout_constraintEnd_toStartOf="@id/button2"
		app:layout_constraintStart_toStartOf="parent"
		app:layout_constraintTop_toTopOf="parent"/>

	<Button
		android:id="@+id/button2"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:text="ボタン2"
		android:textSize="20sp"
		app:layout_constraintEnd_toEndOf="parent"
		app:layout_constraintStart_toEndOf="@id/button1"
		app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
```

次に、カスタムビュー用の処理を作成。
* `BindingAdapter`アノテーションによりxml内での指定が可能になる。
* 引数に`OnClickListener`インターフェースを使用することで、複数のボタンリスナーを検知することができる。
```CustomView.kt
class CustomView @JvmOverloads constructor(
	context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
	init {
		ViewCustomBinding.inflate(LayoutInflater.from(context), this, true)
	}

	companion object {
		interface OnClickListener {
			fun onClick()
		}

		@JvmStatic
		@BindingAdapter("onButton1Click")
		fun setOnButton1Click(view: View?, listener: OnClickListener?) {
			view?.findViewById<Button>(R.id.button1)?.setOnClickListener {
				listener?.onClick()
			}
		}

		@JvmStatic
		@BindingAdapter("onButton2Click")
		fun setOnButton2Click(view: View?, listener: OnClickListener?) {
			view?.findViewById<Button>(R.id.button2)?.setOnClickListener {
				listener?.onClick()
			}
		}
	}
}
```

ViewModelの作成。表示するテキストと、ボタン押下時の処理を作成。
```MainViewModel.kt
class MainViewModel {

	private val _text = MutableLiveData("")

	val text: LiveData<String>
		get() = _text

	fun button1Click() {
		_text.value = "1"
	}

	fun button2Click() {
		_text.value = "2"
	}
}
```

メインのビュー作成。先ほど作成したカスタムビューの配置とViewModelを付与する。
```activity_main.xml
<?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">

	<data>

		<variable
			name="viewModel"
			type="com.example.testapplication.MainViewModel"/>
	</data>

	<androidx.constraintlayout.widget.ConstraintLayout
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		android:background="@color/white">

		<TextView
			android:id="@+id/text"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="@{viewModel.text}"
			android:textSize="20sp"
			app:layout_constraintBottom_toTopOf="@id/custom_view"
			app:layout_constraintEnd_toEndOf="parent"
			app:layout_constraintStart_toStartOf="parent"
			app:layout_constraintTop_toTopOf="parent"/>

		<com.example.testapplication.CustomView
			android:id="@+id/custom_view"
			android:layout_width="0dp"
			android:layout_height="wrap_content"
			app:layout_constraintBottom_toBottomOf="parent"
			app:layout_constraintEnd_toEndOf="parent"
			app:layout_constraintStart_toStartOf="parent"
			app:onButton1Click="@{()->viewModel.button1Click()}"
			app:onButton2Click="@{()->viewModel.button2Click()}"
			app:layout_constraintTop_toBottomOf="@id/text"/>
	</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
```

最後に、ビュー内の処理を追加。
```MainActivity.kt
class MainActivity : AppCompatActivity() {

	/** ViewBinding */
	private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(binding.root)

		binding.viewModel = MainViewModel()
		binding.lifecycleOwner = this
	}
}
```