このレッスンでは、タッチイベントの動きをトラッキングする方法について説明します。
新しい onTouchEvent()
は、現在のタップ接触位置、圧力、またはサイズが変更されるたびに、ACTION_MOVE
イベントでトリガーされます。一般的な操作の検出で説明しているように、このようなイベントはすべて onTouchEvent()
の MotionEvent
パラメータに記録されます。
指ベースのタップは必ずしも正確な操作形式ではないため、タッチイベントの検出は多くの場合、単純な接触ではなく動きに基づいて行われます。アプリが動きベースの操作(スワイプなど)と動きベースでない操作(シングルタップなど)を区別できるように、Android には「タッチスロップ」の概念があります。タッチスロップとは、ユーザーのタップが移動ベースの操作として解釈されるまでにタップが動ける距離を、ピクセル単位で表したものです。このトピックの詳細については、ViewGroup のタップイベントの管理をご覧ください。
操作の動きをトラッキングする方法は、アプリケーションのニーズに応じていくつかあります。次に例を示します。
- ポインタの開始位置と終了位置(たとえば、画面上のオブジェクトをポイント A からポイント B に移動する)。
- x 座標と y 座標によって求まるポインタの移動方向。
- 履歴。操作の履歴のサイズを確認するには、
MotionEvent
メソッドのgetHistorySize()
を呼び出します。その後、モーション イベントのgetHistorical<Value>
メソッドを使用して、各履歴イベントの位置、サイズ、時間、圧力を取得できます。履歴は、タッチ描画などでユーザーの指の軌跡をレンダリングするときに役立ちます。詳しくは、MotionEvent
リファレンスをご覧ください。 - ポインタがタッチ スクリーン上を移動するときの速度。
以下の関連リソースもご覧ください。
- 入力イベント API ガイド
- センサーの概要
- ビューのインタラクティブ機能の作成
トラッキング速度
ポインタが移動した距離や方向のみに基づく移動ベースの操作を使用できます。しかし速度は多くの場合、操作の特性をトラッキングする際の、または操作が発生したかどうかを判断する際の、決定要因です。速度の計算を簡単にするために、Android には VelocityTracker
クラスが用意されています。VelocityTracker
は、タッチイベントの速度のトラッキングに役立ちます。速度が操作の基準の一部である操作(フリングなど)に便利です。
VelocityTracker
API のメソッドの目的を示す簡単な例を次に示します。
Kotlin
private const val DEBUG_TAG = "Velocity" class MainActivity : Activity() { private var mVelocityTracker: VelocityTracker? = null override fun onTouchEvent(event: MotionEvent): Boolean { when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { // Reset the velocity tracker back to its initial state. mVelocityTracker?.clear() // If necessary retrieve a new VelocityTracker object to watch the // velocity of a motion. mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain() // Add a user's movement to the tracker. mVelocityTracker?.addMovement(event) } MotionEvent.ACTION_MOVE -> { mVelocityTracker?.apply { val pointerId: Int = event.getPointerId(event.actionIndex) addMovement(event) // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() // and getYVelocity() to retrieve the velocity for each pointer ID. computeCurrentVelocity(1000) // Log velocity of pixels per second // Best practice to use VelocityTrackerCompat where possible. Log.d("", "X velocity: ${getXVelocity(pointerId)}") Log.d("", "Y velocity: ${getYVelocity(pointerId)}") } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { // Return a VelocityTracker object back to be re-used by others. mVelocityTracker?.recycle() mVelocityTracker = null } } return true } }
Java
public class MainActivity extends Activity { private static final String DEBUG_TAG = "Velocity"; ... private VelocityTracker mVelocityTracker = null; @Override public boolean onTouchEvent(MotionEvent event) { int index = event.getActionIndex(); int action = event.getActionMasked(); int pointerId = event.getPointerId(index); switch(action) { case MotionEvent.ACTION_DOWN: if(mVelocityTracker == null) { // Retrieve a new VelocityTracker object to watch the // velocity of a motion. mVelocityTracker = VelocityTracker.obtain(); } else { // Reset the velocity tracker back to its initial state. mVelocityTracker.clear(); } // Add a user's movement to the tracker. mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() // and getYVelocity() to retrieve the velocity for each pointer ID. mVelocityTracker.computeCurrentVelocity(1000); // Log velocity of pixels per second // Best practice to use VelocityTrackerCompat where possible. Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId)); Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId)); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // Return a VelocityTracker object back to be re-used by others. mVelocityTracker.recycle(); break; } return true; } }
ポインタ キャプチャの使用
ゲーム、リモート デスクトップ、仮想化クライアントなど一部のアプリでは、マウスポインタの制御から大きなメリットを得ています。ポインタ キャプチャは Android 8.0(API レベル 26)以降で利用可能な機能であり、すべてのマウスイベントをアプリ内のフォーカスされているビューに配信することで、このような制御を実現します。
ポインタ キャプチャのリクエスト
アプリのビューは、そのビューを含むビュー階層にフォーカスがある場合にのみ、ポインタ キャプチャをリクエストできます。このため、onClick()
イベント中または onWindowFocusChanged()
イベント ハンドラなど、ビューに特定のユーザー アクションがあるときは、ポインタ キャプチャをリクエストする必要があります。
ポインタ キャプチャをリクエストするには、ビューで requestPointerCapture()
メソッドを呼び出します。次のコード例は、ユーザーがビューをクリックしたときにポインタ キャプチャをリクエストする方法を示しています。
Kotlin
fun onClick(view: View) { view.requestPointerCapture() }
Java
@Override public void onClick(View view) { view.requestPointerCapture(); }
ポインタをキャプチャするリクエストが成功すると、Android は onPointerCaptureChange(true)
を呼び出します。キャプチャをリクエストしたビューと同じビュー階層内であれば、システムはマウスイベントをアプリ内のフォーカスされているビューに配信します。他のアプリは、ACTION_OUTSIDE
イベントを含め、キャプチャがリリースされるまでマウスイベントの受信を停止します。Android は通常、マウス以外のソースからポインタ イベントを配信しますが、これでマウスポインタが表示されなくなります。
キャプチャされたポインタ イベントの処理
ビューがポインタ キャプチャを正常に取得すると、Android はマウスイベントの配信を開始します。フォーカスされているビューでは、次のタスクのいずれかを実施することでイベントを処理できます。
- カスタムビューを使用している場合は、
onCapturedPointerEvent(MotionEvent)
をオーバーライドします。 - それ以外の場合は、
OnCapturedPointerListener
を登録します。
次のコード例は、onCapturedPointerEvent(MotionEvent)
を実装する方法を示しています。
Kotlin
override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean { // Get the coordinates required by your app val verticalOffset: Float = motionEvent.y // Use the coordinates to update your view and return true if the event was // successfully processed return true }
Java
@Override public boolean onCapturedPointerEvent(MotionEvent motionEvent) { // Get the coordinates required by your app float verticalOffset = motionEvent.getY(); // Use the coordinates to update your view and return true if the event was // successfully processed return true; }
次のコード例は、OnCapturedPointerListener
を登録する方法を示しています。
Kotlin
myView.setOnCapturedPointerListener { view, motionEvent -> // Get the coordinates required by your app val horizontalOffset: Float = motionEvent.x // Use the coordinates to update your view and return true if the event was // successfully processed true }
Java
myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() { @Override public boolean onCapturedPointer (View view, MotionEvent motionEvent) { // Get the coordinates required by your app float horizontalOffset = motionEvent.getX(); // Use the coordinates to update your view and return true if the event was // successfully processed return true; } });
カスタムビューを使用するかリスナーを登録するかに関係なく、トラックボール デバイスによって配信される座標と同様に、X / Y デルタなどの相対的な動きを指定するポインタ座標を持つ MotionEvent
をビューが受け取ります。getX()
と getY()
を使用することで座標を取得できます。
ポインタ キャプチャの解除
次のコード例に示すように、アプリのビューは、releasePointerCapture()
を呼び出すことでポインタ キャプチャを解放できます。
Kotlin
override fun onClick(view: View) { view.releasePointerCapture() }
Java
@Override public void onClick(View view) { view.releasePointerCapture(); }
システムは、releasePointerCapture()
を明示的に呼び出さなくても、ビューからキャプチャを取り除くことができます。これはほとんどの場合、キャプチャをリクエストしたビューを含むビュー階層がフォーカスを失ったためです。