準備
こういう拡張関数を作っておく。(DataBinding 使わないなら @BindingAdapter("onSafeClick")
の行は不要)
@BindingAdapter("android:onSafeClick")
fun View.setOnSafeClickListener(listener: View.OnClickListener) {
this.setOnClickListener {
it.isClickable = false
it.postDelayed(300) { // 300ms内の連続クリックを無効に
it.isClickable = true
}
listener.onClick(it)
}
}
isClickable
による制御が気になる場合は、this.setOnClickListener {
の外側で前回クリックのタイムスタンプを保持しておいて比較、という対応でもいいかもしれない。
利用例
DataBinding を使う場合は次のようにリスナーをセットする。
<!-- View ならばなんでもいい -->
<TextView
android:id="@+id/some_text_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="テスト"
android:onSafeClick="@{() -> someViewModel.onSomeTextClicked()}"
/>
DataBinding 使わないなら、コードで次のようにリスナーをセットする。
someTextView.setOnSafeClickListener(View.OnClickListener {
someViewModel.onSomeTextClicked()
})
コードでリスナーをセットする場合、次のような拡張関数を用意しておくとよりすっきり書ける。
fun View.setOnSafeClickListener(block: (View) -> Unit) {
this.setOnSafeClickListener(View.OnClickListener {
block.invoke(it)
})
}
// ...
someTextView.setOnSafeClickListener {
someViewModel.onSomeTextClicked()
}
補足
DataBinding を使う場合で、任意の引数を渡す場合は次のように書く。(この例だと整数1
を渡している。)
android:onSafeClick="@{() -> someViewModel.onSomeTextClicked(1)}"
View のインスタンスを渡す場合は次のように。
android:onSafeClick="@{(view) -> someViewModel.onSomeTextClicked(view)}"
この場合(View のインスタンスを渡す場合)は、メソッドリファレンスに置き換えられる。
android:onSafeClick="@{someViewModel::onSomeTextClicked}"
改善案
300
ms という数字を引数で渡せるようにするともっと便利かもしれない。(DataBinding でリスナーと数字、というふうに複数の引数を渡す必要があるので、その方法を調べる。)