batchのブログ

知見の備忘録

Androidで画面のスワイプや物理キーイベントなどを取得する with Kotlin

はじめに

Androidの画面のタッチイベントとかって気になりつつもあまり触れることって少ないですよね.

先日のハッカソンでばりばり触る機会があり,いろいろ調べて実装したので,画面のタップ,2本指タップ,長押し,スワイプなどの実装の仕方の知見を今後自分がまた実装するときのメモと世の中のAndroidアプリエンジニアに共有していくことを兼ねて書いていきたいと思います.

画面タッチ全般

大方,onTouchEventoverrideすることで取得することができます.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.ListenerimplementしてswipeMotionoverrideしてwhenを使って上下左右どの向きにスワイプされたのか受け取り,それぞれに応じた処理を書いてあげます.たしか,スワイプされたときのバイブレーションはSwipeDetectクラスでやりたいけど,getSystemService(Context.VIBRATOR_SERVICE) as VibratorするときにContextが必要で,SwipeDetectにはContextがなくて実装できないのでSwipeDetectvibinterfaceとして書いて,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)
    }
}

さいごに

なにかコードに間違いや,こうしたらもうちょっとスマートに書けるよなどあればぜひ教えていただきたいです.
Android開発は本当に楽しいです.
みんなでAndroid開発もっと楽しんでいきましょう.