ユーザーがタッチ スクリーンに指を 1 本以上置くと「タップ操作」が発生し、アプリはそのタップのパターンを特定の操作として解釈します。これに対応して、ジェスチャ検出には 2 つのフェーズがあります。
- タップイベントに関するデータを収集します。
- データを解釈して、アプリがサポートする操作のいずれかの基準を満たしているかどうかを確認します。
以下の関連リソースをご覧ください。
- 入力イベント API ガイド
- センサーの概要
- ビューのインタラクティブ機能の作成
サポート ライブラリ クラス
このレッスンでは、例として GestureDetectorCompat
クラスと MotionEventCompat
クラスを取り上げます。これらのクラスはサポート ライブラリにあります。サポート ライブラリ クラスは、Android 1.6 以上を搭載したデバイスとの互換性を提供できる場合に使用してください。MotionEventCompat
は、MotionEvent
の後継クラスではありません。その代わりに、MotionEvent
オブジェクトを渡すことができる静的ユーティリティ メソッドを提供します。それにより、そのイベントに関連する目的のアクションを受け取れます。
データの収集
ユーザーが画面に 1 本以上の指を置くと、タップイベントを受け取ったビューでコールバック onTouchEvent()
がトリガーされます。
最終的に操作として識別されるタップイベントのシーケンス(位置、圧力、サイズ、別の指を置く動作など)ごとに、onTouchEvent()
が数回発生します。
操作は、ユーザーが最初に画面に触れたときに始まり、システムがユーザーの指の位置を追跡している間継続し、最後のイベント(ユーザーの指が画面を離れる)をキャプチャしたときに終わります。このインタラクション全体を通して、onTouchEvent()
に配信される MotionEvent
にすべてのインタラクションの詳細が示されます。アプリは、MotionEvent
によって提供されるデータを使用して、処理すべき操作が発生したかどうかを判断できます。
Activity または View のタップイベントをキャプチャする
Activity または View のタップイベントをインターセプトするには、onTouchEvent()
コールバックをオーバーライドします。
次のスニペットは、getActionMasked()
を使用して、ユーザーが実行したアクションを event
パラメータから抽出します。これにより、処理すべき操作が発生したかどうかを判断するために必要な RAW データが得られます。
Kotlin
class MainActivity : Activity() { ... // This example shows an Activity, but you would use the same approach if // you were subclassing a View. override fun onTouchEvent(event: MotionEvent): Boolean { val action: Int = MotionEventCompat.getActionMasked(event) return when (action) { MotionEvent.ACTION_DOWN -> { Log.d(DEBUG_TAG, "Action was DOWN") true } MotionEvent.ACTION_MOVE -> { Log.d(DEBUG_TAG, "Action was MOVE") true } MotionEvent.ACTION_UP -> { Log.d(DEBUG_TAG, "Action was UP") true } MotionEvent.ACTION_CANCEL -> { Log.d(DEBUG_TAG, "Action was CANCEL") true } MotionEvent.ACTION_OUTSIDE -> { Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element") true } else -> super.onTouchEvent(event) } } }
Java
public class MainActivity extends Activity { ... // This example shows an Activity, but you would use the same approach if // you were subclassing a View. @Override public boolean onTouchEvent(MotionEvent event){ int action = MotionEventCompat.getActionMasked(event); switch(action) { case (MotionEvent.ACTION_DOWN) : Log.d(DEBUG_TAG,"Action was DOWN"); return true; case (MotionEvent.ACTION_MOVE) : Log.d(DEBUG_TAG,"Action was MOVE"); return true; case (MotionEvent.ACTION_UP) : Log.d(DEBUG_TAG,"Action was UP"); return true; case (MotionEvent.ACTION_CANCEL) : Log.d(DEBUG_TAG,"Action was CANCEL"); return true; case (MotionEvent.ACTION_OUTSIDE) : Log.d(DEBUG_TAG,"Movement occurred outside bounds " + "of current screen element"); return true; default : return super.onTouchEvent(event); } }
次に、このようなイベントを独自に処理して、操作が発生したかどうかを判断できます。これはカスタム操作に対して行う必要がある処理の一種です。ただし、ダブルタップ、長押し、フリングなどの一般的な操作をアプリで使用する場合は、GestureDetector
クラスを利用できます。GestureDetector
を使用すると、個々のタップイベントを独自に処理しなくても、一般的な操作を簡単に検出できます。これについては、操作の検出で説明しています。
単一のビューのタップイベントをキャプチャする
onTouchEvent()
を使用する代わりに、setOnTouchListener()
メソッドを使用して View.OnTouchListener
オブジェクトを任意の View
オブジェクトにアタッチできます。これにより、既存の View
をサブクラス化しなくても、タップイベントをリッスンできます。例:
Kotlin
findViewById<View>(R.id.my_view).setOnTouchListener { v, event -> // ... Respond to touch events true }
Java
View myView = findViewById(R.id.my_view); myView.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { // ... Respond to touch events return true; } });
ACTION_DOWN
イベントに対して false
を返すリスナーの作成には注意が必要です。これを行うと、その後の連続するイベント ACTION_MOVE
および ACTION_UP
に対してリスナーが呼び出されません。これは、ACTION_DOWN
がすべてのタップイベントの開始点であるためです。
カスタムビューを作成する場合は、上記のように onTouchEvent()
をオーバーライドできます。
操作の検出
Android には、一般的な操作を検出するための GestureDetector
クラスが用意されています。サポートされている操作には、onDown()
、onLongPress()
、onFling()
などがあります。GestureDetector
は、上記の onTouchEvent()
メソッドと組み合わせて使用できます。
サポートされている操作をすべて検出する
GestureDetectorCompat
オブジェクトをインスタンス化する際は、GestureDetector.OnGestureListener
インターフェースを実装するクラスをパラメータの 1 つとして取ります。GestureDetector.OnGestureListener
は、特定のタップイベントが発生したときにユーザーに通知します。GestureDetector
オブジェクトがイベントを受け取れるようにするには、View または Activity の onTouchEvent()
メソッドをオーバーライドし、観測されたすべてのイベントを Detector インスタンスに渡します。
次のスニペットで、個々の on<TouchEvent>
メソッドの戻り値 true
は、タップイベントが正常に処理されたことを示します。戻り値 false
は、タップが正常に処理されるまでイベントがビュースタックに渡されることを示します。
次のスニペットを実行して、タッチ スクリーンを操作したときにアクションがどのようにトリガーされるか、各タップイベントに対する MotionEvent
の内容は何かを把握してください。単純な操作であっても、いかに多くのデータが生成されるかを理解できます。
Kotlin
private const val DEBUG_TAG = "Gestures" class MainActivity : Activity(), GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { private lateinit var mDetector: GestureDetectorCompat // Called when the activity is first created. public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener mDetector = GestureDetectorCompat(this, this) // Set the gesture detector as the double tap // listener. mDetector.setOnDoubleTapListener(this) } override fun onTouchEvent(event: MotionEvent): Boolean { return if (mDetector.onTouchEvent(event)) { true } else { super.onTouchEvent(event) } } 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 } override fun onLongPress(event: MotionEvent) { Log.d(DEBUG_TAG, "onLongPress: $event") } override fun onScroll( event1: MotionEvent, event2: MotionEvent, distanceX: Float, distanceY: Float ): Boolean { Log.d(DEBUG_TAG, "onScroll: $event1 $event2") return true } override fun onShowPress(event: MotionEvent) { Log.d(DEBUG_TAG, "onShowPress: $event") } override fun onSingleTapUp(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapUp: $event") return true } override fun onDoubleTap(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTap: $event") return true } override fun onDoubleTapEvent(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTapEvent: $event") return true } override fun onSingleTapConfirmed(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event") return true } }
Java
public class MainActivity extends Activity implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener{ private static final String DEBUG_TAG = "Gestures"; private GestureDetectorCompat mDetector; // Called when the activity is first created. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener mDetector = new GestureDetectorCompat(this,this); // Set the gesture detector as the double tap // listener. mDetector.setOnDoubleTapListener(this); } @Override public boolean onTouchEvent(MotionEvent event){ if (this.mDetector.onTouchEvent(event)) { return true; } return super.onTouchEvent(event); } @Override public boolean onDown(MotionEvent event) { Log.d(DEBUG_TAG,"onDown: " + event.toString()); return true; } @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString()); return true; } @Override public void onLongPress(MotionEvent event) { Log.d(DEBUG_TAG, "onLongPress: " + event.toString()); } @Override public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) { Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString()); return true; } @Override public void onShowPress(MotionEvent event) { Log.d(DEBUG_TAG, "onShowPress: " + event.toString()); } @Override public boolean onSingleTapUp(MotionEvent event) { Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString()); return true; } @Override public boolean onDoubleTap(MotionEvent event) { Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString()); return true; } @Override public boolean onDoubleTapEvent(MotionEvent event) { Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString()); return true; } @Override public boolean onSingleTapConfirmed(MotionEvent event) { Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString()); return true; } }
サポートされている操作のサブセットを検出する
少数の操作のみを処理する場合は、GestureDetector.OnGestureListener
インターフェースを実装する代わりに GestureDetector.SimpleOnGestureListener
を拡張できます。
GestureDetector.SimpleOnGestureListener
は、すべての on<TouchEvent>
メソッドに対して false
を返すことにより、それらのメソッドすべての実装を提供します。これにより、必要なメソッドのみをオーバーライドできます。たとえば、以下のスニペットは、GestureDetector.SimpleOnGestureListener
を拡張して onFling()
と onDown()
をオーバーライドするクラスを作成します。
GestureDetector.OnGestureListener
を使用するかどうかにかかわらず、true
を返す onDown()
メソッドを実装することをおすすめします。これは、すべての操作が onDown()
メッセージで始まるためです。onDown()
から false
を返す場合、GestureDetector.SimpleOnGestureListener
のデフォルトの動作と同様に、システムは残りの操作が無視されると想定し、GestureDetector.OnGestureListener
の他のメソッドは呼び出されなくなります。これにより、アプリで予期しない問題が発生する可能性があります。onDown()
から false
を返すのは、操作全体を実際に無視する場合のみにしてください。
Kotlin
private const val DEBUG_TAG = "Gestures" class MainActivity : Activity() { private lateinit var mDetector: GestureDetectorCompat public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mDetector = GestureDetectorCompat(this, MyGestureListener()) } override fun onTouchEvent(event: MotionEvent): Boolean { mDetector.onTouchEvent(event) return super.onTouchEvent(event) } 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 } } }
Java
public class MainActivity extends Activity { private GestureDetectorCompat mDetector; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDetector = new GestureDetectorCompat(this, new MyGestureListener()); } @Override public boolean onTouchEvent(MotionEvent event){ this.mDetector.onTouchEvent(event); return super.onTouchEvent(event); } class MyGestureListener extends GestureDetector.SimpleOnGestureListener { private static final String DEBUG_TAG = "Gestures"; @Override public boolean onDown(MotionEvent event) { Log.d(DEBUG_TAG,"onDown: " + event.toString()); return true; } @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString()); return true; } } }