GestureDetectorを使ったジェスチャーのハンドリング
概要
GestureDetector
を使うことで、共通のジェスチャーを簡単にハンドルすることができる。
またActivity.onTouchEvent()
もしくはView.onTouchEvent()
と組み合わせて使うため、それ以外のジェスチャーのハンドリングもシンプルに記述することが可能となっている。
ハンドルできるジェスチャー
- シングルタップ
- ダブルタップ
- フリック
- スクロール - フリックの途中も呼び出されるため、フリックとの組み合わせは注意が必要
使い方
- インスタンスの生成とタッチイベントのハンドリング
GestureDetector.OnGestureListener
の実装- ダブルタップをサポートする場合は、
GestureDetector.OnDoubleTapListener
を実装
1. インスタンスの生成とハンドリング
タッチイベントをハンドリングするために、GestureDetector
の上位互換であるGestureDetectorCompat
インスタンスを生成し、Activity.onTouchEvent()
もしくは、View.onTouchEvent()
でハンドリングする。
class MainActivity : AppCompatActivity(), GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { lateinit var gestureDetector: GestureDetectorCompat override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) gestureDetector = GestureDetectorCompat(this, this) gestureDetector.setOnDoubleTapListener(this) }
GestureDetectorCompat(context, listener)
gestureDetector.setOnDoubleTapListener(this)
ダブルタップを有効にする場合
override fun onTouchEvent(event: MotionEvent?): Boolean { // GestureDetector側でハンドリングされた場合は、trueが返される if (gestureDetector.onTouchEvent(event)) { return true } else { // GestureDetector側でハンドリングされなかった場合は、それ以外のタッチジェスチャーがこちらでハンドリングできる return super.onTouchEvent(event) } }
2. GestureDetector.OnGestureListener
の実装
以下のメソッドを全てオーバーライドする必要がある
//region GestureDetector.OnGestureListener // MothionEvent.DONWのハンドリング override fun onDown(e: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDown: $e") return true } override fun onShowPress(e: MotionEvent) { Log.d(DEBUG_TAG, "onShowPress: $e") } override fun onSingleTapUp(e: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapUp: $e") return true } override fun onLongPress(e: MotionEvent) { Log.d(DEBUG_TAG, "onLongPress: $e") } override fun onFling( e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { Log.d(DEBUG_TAG, "onFling: $e1 $e2") return true } override fun onScroll( e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float ): Boolean { Log.d(DEBUG_TAG, "onScroll: $e1 $e2") return true } //endregion
ダブルタップの検知もサポートする場合
GestureDetector.OnDoubleTapListener
を実装する
以下のメソッドをオーバーライドする
//region GestureDetector.OnDoubleTapListener override fun onSingleTapConfirmed(e: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapConfirmed: $e") return true } override fun onDoubleTap(e: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTap: $e") return true } override fun onDoubleTapEvent(e: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTapEvent: $e") return true } //endregion
イベントのハンドリング
シングルタップ
onDown: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=563.9612, y[0]=1256.9531, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=135467577, downTime=135467577, deviceId=11, source=0x1002, displayId=0 } --- onDownされたのでactivity or viewに結果をtrueで返す onShowPress: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=563.9612, y[0]=1256.9531, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=135467577, downTime=135467577, deviceId=11, source=0x1002, displayId=0 } onSingleTapUp: MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=563.9612, y[0]=1256.9531, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=135467700, downTime=135467577, deviceId=11, source=0x1002, displayId=0 } --- ダブルタップの可能性があるため一旦activity or viewにtrueが返る 最後にシングルタップの検知 onSingleTapConfirmed: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=563.9612, y[0]=1256.9531, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=135467577, downTime=135467577, deviceId=11, source=0x1002, displayId=0 }
ダブルタップの検知
onDown: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=359.97803, y[0]=841.9336, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=136774823, downTime=136774823, deviceId=11, source=0x1002, displayId=0 } --- onDownの結果が返る onSingleTapUp: MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=359.97803, y[0]=843.9258, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=136774912, downTime=136774823, deviceId=11, source=0x1002, displayId=0 } --- onSingleToupの結果が返る onDoubleTap: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=359.97803, y[0]=841.9336, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=136774823, downTime=136774823, deviceId=11, source=0x1002, displayId=0 } onDoubleTapEvent: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=359.97803, y[0]=843.9258, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=136774991, downTime=136774991, deviceId=11, source=0x1002, displayId=0 } onDown: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=359.97803, y[0]=843.9258, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=136774991, downTime=136774991, deviceId=11, source=0x1002, displayId=0 } --- onDownの結果が返る onDoubleTapEvent: MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=359.97803, y[0]=843.9258, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=136775082, downTime=136774991, deviceId=11, source=0x1002, displayId=0 } --- onDoubleTapEvent action=ACTION_UPの結果が返る
onDoubleTapEvent()
が、開始ACTION_DOWN
と最後ACTION_UP
の2回呼ばれるonSingleTap()
が呼ばれているが、onSingleTapConfirmed()
は呼ばれていない
フリック
onDown: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=585.9778, y[0]=1256.9531, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=135656229, downTime=135656229, deviceId=11, source=0x1002, displayId=0 } --- onDonwの結果が返される onScroll: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=585.9778, y[0]=1256.9531, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=135656229, downTime=135656229, deviceId=11, source=0x1002, displayId=0 } MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=585.9778, y[0]=1228.0225, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=2, eventTime=135656277, downTime=135656229, deviceId=11, source=0x1002, displayId=0 } --- onScrollの結果が返される onScroll: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=585.9778, y[0]=1256.9531, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=135656229, downTime=135656229, deviceId=11, source=0x1002, displayId=0 } MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=585.9778, y[0]=1189.4066, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=1, eventTime=135656294, downTime=135656229, deviceId=11, source=0x1002, displayId=0 } --- onScrollの結果が返される onScroll: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=585.9778, y[0]=1256.9531, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=135656229, downTime=135656229, deviceId=11, source=0x1002, displayId=0 } MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=585.9778, y[0]=1154.9127, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=2, eventTime=135656310, downTime=135656229, deviceId=11, source=0x1002, displayId=0 } --- onScrollの結果が返される ... onScrollが何度か呼び出される --- onScrollの結果が返される onFling: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=585.9778, y[0]=1256.9531, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=135656229, downTime=135656229, deviceId=11, source=0x1002, displayId=0 } MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=574.9695, y[0]=407.9297, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=135656476, downTime=135656229, deviceId=11, source=0x1002, displayId=0 } dle the touch event successfuly --- onFlingの結果が返される
- フリックの途中の
onScroll
(指の移動)が呼び出される
SimpleOnGestureListenerを使う方法
OnGestureListenr
は使用しないジェスチャーもオーバーライドする必要があるが、SimpleOnGestureListener
を使用することで、
必要な分だけオーバーライドする事ができる。
ただしこちらはクラスなのでActivityやViewに直接実装することはできない。
フリックのハンドリングのみ実装できる
private class MyGestureListener : GestureDetector.SimpleOnGestureListener() { override fun onDown(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDown: $event") return true } override fun onFling( event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { Log.d(DEBUG_TAG, "onFling: $event1 $event2") return true } }