はじめに
Buttonをダブルタップしたときに処理が2回実行されてしまうことってありますよね。
例えばActivityの遷移やFragmentの生成など1回でよいときは非常に困ることがあります。
今回はそのような問題を解決するための処理を作成していこうと思います。
特にMVVMでDataBindingを使ったサンプルが無かったので共有できたらよいです…
どのような問題が起きているか?
次のようなButtonをクリックするカウントが増減するアプリがあったとします。
見るとわかるとおりダブルタップすると2回カウントしてしまっています。
ちなみにですがコードはだいたいこんな感じです。
DataBindingを使った場合の対処法ということでMVVMで作ってます。
View
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val viewModel = ViewModelProviders.of(this).get(MainActivityViewModel::class.java)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.vm = viewModel
binding.lifecycleOwner = this
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="vm" type="kaleidot725.safeclicksample.MainActivityViewModel"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="6"
android:layout_gravity="center"
android:gravity="center"
android:textSize="100dp"
android:text="@{vm.count}"/>
<Button
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="plus"
android:onClick="@{vm::plus}"/>
<Button
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="minus"
android:onClick="@{vm::minus}"/>
</LinearLayout>
</layout>
ViewModel
class MainActivityViewModel : ViewModel() {
private val counter : Counter = Counter()
private val _count : MutableLiveData<String> = MutableLiveData()
val count : LiveData<String> get() = _count
init {
_count.value = counter.num.toString()
}
fun plus(v : View) {
counter.plus()
_count.value = counter.num.toString()
}
fun minus(v : View) {
counter.minus()
_count.value = counter.num.toString()
}
}
Model
class Counter {
var num : Int = 0
private set
get
fun plus() {
num++
}
fun minus() {
num--
}
}
この問題をどうやって解決するか?
調べますとダブルタップしたときに2回目の処理を実行させないために、
1000ms以内にButtonがもう一度押された場合は実行しないようにする方法があるようです。
🔗 How to resolve double tap on Button Issue in android?
これをDataBindingを使って上手くやりたいので、1000ms以内にButtonが
もう一度押された場合に実行しないようにするBindingAdapterを作ります。
では実際に実装していってみたいと思います。
kotlin-kaptを有効にする
BindingAdapterを作るときにはkotlin-kaptが有効になっていなければなりませんので、
build.gradle(Module:app)に次のないようを記述しておきます。
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
︙
}
︙
BindingAdapterを作成する
BindingAdatperを次のようにもし前回押した時刻から
1000ms経過していなければ何もせずにreturnする処理を実装します。
@BindingAdapter("app:onSafeClick")
fun onSafeClick(view: View, listener : View.OnClickListener) {
class SafeClickListener(
private var defaultInterval: Int = 1000,
private val listener : View.OnClickListener) : View.OnClickListener
{
private var lastTimeClicked: Long = 0
override fun onClick(v: View) {
if (SystemClock.elapsedRealtime() - lastTimeClicked < defaultInterval) {
return
}
lastTimeClicked = SystemClock.elapsedRealtime()
listener.onClick(v)
}
}
view.setOnClickListener(SafeClickListener(1000, listener))
}
XMLのDataBindingを変更する
そしてあとはxmlでDataBindingしているonClickの記述を
onSafeClickに変更してあげれば完成です。
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="vm" type="kaleidot725.safeclicksample.MainActivityViewModel"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="6"
android:layout_gravity="center"
android:gravity="center"
android:textSize="100dp"
android:text="@{vm.count}"/>
<Button
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="plus(safe)"
app:onSafeClick="@{vm::plus}"/>
<Button
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="minus(safe)"
app:onSafeClick="@{vm::minus}"/>
</LinearLayout>
</layout>
動作を確認する
実行してみますと次のような感じになります。
ダブルタップしても1回のみカウントしてますね。
1000msと大雑把に定義しましたがこれは変更が必要かもですね…
おわりに
この実装をしたサンプルは次のGitHubリポジトリにあります。
上手く実装できなかったり、詳細がわからない場合は閲覧ください!!