ボタン連打されてしまい不正な動作をするのはよくある話

Android アプリ開発においてボタン、というより View に対しタップイベントを仕込む際に View.OnClickListener() を使用するというのは基本だと思うが、何も考えずに実装すると連打された時にイベントが 2 回、3 回発行されてしまう。 これが問題になってくるのが例えば登録処理などの部分で、データが 2 重に登録されてしまったり既に登録されている為不正なレスポンスが返却されてきてしまったりする。

対応策に関しては使い古された話題なので検索すればいくらでも出てくるが、例えばお手軽にAndroidのButtonの連打を防止してみたの記事のようにボタン押下直後にボタンの押下を無効にし Handler を使って 1 秒後に再度有効にする、といった処理を挟む。

ただ、毎回このようなお決まりの処理を書くのは辛いので Kotlin の Extension を使って共通化してみた、という記事である。

実装例

このような Extension を定義する:

/**
 * 短時間での連打による複数回実行を防ぐ setOnClickListener 実装.
  *
  * @param listener setOnClickListener
  */
 fun View.setOnSingleClickListener(listener: () -> Unit) {
     val delayMillis = 1000 // 二度押しを防止する時間
     var pushedAt = 0L
     setOnClickListener {
         if (System.currentTimeMillis() - pushedAt < delayMillis) return@setOnClickListener
         pushedAt = System.currentTimeMillis()
         listener()
     }
 }

私は Handler ではなく pushedAt という押下時の時間を持たせて押下時に現在時刻と比較することにより 1 秒以内の連打を防止する実装を行った。 勿論多くの記事がやっているように Handler でも良いが個人的には Handler インスタンスをその都度 new するより生成コストの低い Long を持たせる方が好きだ。

これを以下のように使用できる:

val register = view?.findViewById<AppCompatButton>(R.id.register)
register.setOnSingleClickListener {
    // 連打してはいけない登録処理...
}