Android:ダブルタップを検知する処理
はじめに
Androidアプリを作っている際に、ダブルタップの検知を行う必要がありました。
そのやり方を簡単に書きます。下記はその動作例です。ボタンをシングルタップするとSnackbarで「One tapped.」と表示、ダブルタップすると「Double tapped.」と表示されます。
やり方
ソースコード
ソースコードは下記のようになります。
MainActivity.kt
class MainActivity : AppCompatActivity() {
// クリック回数を保持する変数
private var numButtonClick : Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setTitle("Dialog Test")
// 表示する画面(xmlファイル)のレイアイとのid名を取得
val mainActivityLayout = findViewById<ConstraintLayout>(R.id.mainActivityLayout)
val button = findViewById<Button>(R.id.button)
button.setOnClickListener{
// カウントアップ
numButtonClick++
Handler(Looper.getMainLooper()).postDelayed({
if(numButtonClick==1){
Snackbar.make(mainActivityLayout, "One tapped.", Snackbar.LENGTH_LONG).show()
}else if(numButtonClick == 2){
Snackbar.make(mainActivityLayout, "Double tapped.", Snackbar.LENGTH_LONG).show()
}
numButtonClick = 0
},300)
}
}
}
activity_main.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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mainActivityLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Double tap"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
ダブルタップの検知はMainActivity.ktで下記のようにおこなっています。
numButtonClick++
Handler(Looper.getMainLooper()).postDelayed({
if(numButtonClick==1){
Snackbar.make(mainActivityLayout, "One tapped.", Snackbar.LENGTH_LONG).show()
}else if(numButtonClick == 2){
Snackbar.make(mainActivityLayout, "Double tapped.", Snackbar.LENGTH_LONG).show()
}
numButtonClick = 0
},300)
HandlerクラスのpostDelayedを呼ぶことで、指定した時間の後に処理を行うことができます。
なので、その時間内にボタンが押されれば押された回数をカウントします。そして指定した時間の後に、if文でその回数に応じて処理を行っています。これでダブルタップの検知を実現できます。
Handler(Looper.getMainLooper()).postDelayed({
// 実行したい処理を記述
},【指定した時間(ミリ秒)】)
deprecationなやり方に注意する
先程のダブルタップ検知部分を下記のようにすると、Handler()がdeprecationと表示されます。
これはHandlerのオブジェクト生成するときに引数なしで使用すると表示されます。
このやり方は非推奨となっています。
numButtonClick++
Handler().postDelayed( {
if(numButtonClick==1){
Log.d("Tittle", "One tapped.")
}else if(numButtonClick == 2){
Log.d("Tittle", "Double tapped.")
}
numButtonClick = 0
}, 300)
Android Developersでは下記ように説明されています。
引数なしでは、暗黙的にLooperが選択され、予期せず終了(クラッシュ)する可能性があるとのことです。
Handler()
This constructor is deprecated. Implicitly choosing a Looper during Handler construction can lead to bugs where operations are silently lost (if the Handler is not expecting new tasks and quits), crashes (if a handler is sometimes created on a thread without a Looper active), or race conditions, where the thread a handler is associated with is not what the author anticipated. Instead, use an Executor or specify the Looper explicitly, using Looper#getMainLooper, {link android.view.View#getHandler}, or similar. If the implicit thread local behavior is required for compatibility, use new Handler(Looper.myLooper()) to make it clear to readers.
Handler - Android Developers
そのため引数なしでは使わず、下記のようにLooper.getMainLooper()を指定して使う方法が推奨されています。
Handler(Looper.getMainLooper())
用語のまとめ
Looperとは内部にメッセージ(命令、タスク)のキューを持ち、順番にキューから取り出したメッセージ(命令、タスク)を処理する仕組みです、そして、Looperにメッセージ(命令、タスク)を届けるためのメッセンジャーがHandlerです。Looperには、メインスレッドのLooperを取得するヘルパー関数「Looper.getMainLooper()」があります。そして、この Looper を使用して 、Handler を作成することでメインスレッド上で処理を行うことができます。
おわりに
上記のやり方でダブルタップを検知することができました。
HandlerやLooperといったように馴染みがないワードだと聞いただけでは、何をするものなのかイメージしづらいです。根気よく理解していくしかありませんね。
参考
- Android 非同期処理についてまとめてみた
-
Android:Kotlin:スレッド処理の基本(Looper と Handler)
- こちらの記事では「スレッドの作成」項目の図がわかりやすかったです。
Discussion