Androidで画面のスワイプや物理キーイベントなどを取得する with Kotlin
はじめに
Androidの画面のタッチイベントとかって気になりつつもあまり触れることって少ないですよね.
先日のハッカソンでばりばり触る機会があり,いろいろ調べて実装したので,画面のタップ,2本指タップ,長押し,スワイプなどの実装の仕方の知見を今後自分がまた実装するときのメモと世の中のAndroidアプリエンジニアに共有していくことを兼ねて書いていきたいと思います.
画面タッチ全般
大方,onTouchEventをoverrideすることで取得することができます.MotionEvent.****でいろんな画面のイベントが取得できるので見てみると楽しいかも.なんとなくざっと実装した例を載せときます.
override fun onTouchEvent(event: MotionEvent?): Boolean { val action = MotionEventCompat.getActionMasked(event) return when (action) { MotionEvent.ACTION_SCROLL -> { motionTextView.text = "ACTION_SCROLL" true } // 2本指以上で触った瞬間呼ばれる MotionEvent.ACTION_POINTER_DOWN -> { motionTextView.text = "ACTION_POINTER_DOWN" true } // 2本指以上で指を離したとき呼ばれる MotionEvent.ACTION_POINTER_UP -> { motionTextView.text = "ACTION_POINTER_UP" true } // 1本指で触ったとき呼ばれる MotionEvent.ACTION_DOWN -> { motionTextView.text = "ACTION_DOWN" true } // 何かしら動かしているとき呼ばれる MotionEvent.ACTION_MOVE -> { motionTextView.text = "ACTION_MOVE" true } else -> super.onTouchEvent(event) } }
バイブレーション
これはめっちゃ簡単.
以下のコード書くだけで鳴らせる.
ただ,鳴らせたいとこにContextなかったりとか色々したら実装がちょっとめんどくさくなる.
また,longArrayOf()の中身を色々変えれば好きなリズムでバイブレーション鳴らすことができます.
vibrator.vibrate(longArrayOf(0, 40, 20, 40), -1)のvibrateがdeprecatedなっていますが,実装できちゃってるので詳しく調べていません・・・
詳しい方はぜひご教授くださいませ・・・・
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator vibrator.vibrate(longArrayOf(0, 40, 20, 40), -1)
物理キーイベント
今回は,音量ボタンが押されたことを検知するコードの実装方法を紹介したいと思います.
以下のコードで音量ボタンのキーイベントを取得できます.
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { when(keyCode) { KeyEvent.KEYCODE_VOLUME_UP -> { // 音量+ボタンが押されたときの処理 } KeyEvent.KEYCODE_VOLUME_DOWN -> { // 音量ーボタンが押されたときの処理 } } return false }
スワイプ
まずは,SwipeDetectクラスとか適当な名前をつけて,スワイプ動作を計算して下にスワイプされたのか,上だったか,右だったか,左だったかを出力してくれるものをつくります.GestureDetector.SimpleOnGestureListener()を継承してあげてください.vib関数はスワイプを検知したときにバイブレーションを鳴らすために書いています.
SwipeDetect.kt
package com.batch.example import android.util.Log import android.view.GestureDetector import android.view.MotionEvent class SwipeDetect(private val listener: Listener) : GestureDetector.SimpleOnGestureListener() { private val MIN_SWIPE_DISTANCE_X = 100 private val MIN_SWIPE_DISTANCE_Y = 100 private val MAX_SWIPE_DISTANCE_X = 2000 private val MAX_SWIPE_DISTANCE_Y = 2000 interface Listener { fun vib() fun swipeMotion(swipe: String) } override fun onFling( e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float ): Boolean { val deltaX: Float val deltaY: Float if (e1?.x != null && e2?.x != null && e1?.y != null && e2?.y != null) { deltaX = e1.x - e2.x deltaY = e1.y - e2.y } else { deltaX = 0.0f deltaY = 0.0f } val deltaXAbs = Math.abs(deltaX) val deltaYAbs = Math.abs(deltaY) if ((deltaXAbs >= MIN_SWIPE_DISTANCE_X) && (deltaXAbs <= MAX_SWIPE_DISTANCE_X)) { if (deltaX > 0) { Log.d("batchSwipe", "Swipe to left") listener.vib() listener.swipeAnim("LEFT") } else { Log.d("batchSwipe", "Swipe to right") listener.vib() listener.swipeAnim("RIGHT") } } else if ((deltaYAbs >= MIN_SWIPE_DISTANCE_Y) && (deltaYAbs <= MAX_SWIPE_DISTANCE_Y)) { if (deltaY > 0) { Log.d("batchSwipe", "Swipe to up") listener.vib() listener.swipeAnim("UP") } else { Log.d("batchSwipe", "Swipe to down") listener.vib() listener.swipeAnim("DOWN") } } return true } }
これができたら,Activity側でSwipeDetect.ListenerをimplementしてswipeMotionをoverrideしてwhenを使って上下左右どの向きにスワイプされたのか受け取り,それぞれに応じた処理を書いてあげます.たしか,スワイプされたときのバイブレーションはSwipeDetectクラスでやりたいけど,getSystemService(Context.VIBRATOR_SERVICE) as VibratorするときにContextが必要で,SwipeDetectにはContextがなくて実装できないのでSwipeDetectにvibをinterfaceとして書いて,ContextがあるMainActivityで実装した形にしたと思います.(ちょっと忘れてる)
MainActivity.kt
package com.batch.example class MainActivity : AppCompatActivity(), AniMoSwipe.Listener { private lateinit var gestureDetectorCompat: GestureDetectorCompat override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_controll) val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator val aniMoSwipe = AniMoSwipe(this) gestureDetectorCompat = GestureDetectorCompat(this, aniMoSwipe) } override fun onTouchEvent(event: MotionEvent?): Boolean { gestureDetectorCompat.onTouchEvent(event) return super.onTouchEvent(event) } override fun swipeMotion(swipe: String) { val animoView = findViewById<ImageView>(R.id.animationView) when (swipe) { "RIGHT" -> { // 右にスワイプしたときの処理 } "LEFT" -> { animationType = "Rotate" // 左にスワイプしたときの処理 } "UP" -> { animationText.text = "なし" // 上にスワイプしたときの処理 } "DOWN" -> { // 下にスワイプしたときの処理 } else -> { } } } override fun vib() { val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator vibrator.vibrate(longArrayOf(0, 30), -1) } }