このレッスンでは、タッチイベントの動きをトラッキングする方法について説明します。
現在のタッチ接触位置、圧力、またはサイズが変更されるたびに、ACTION_MOVE
イベントによって新しい onTouchEvent()
がトリガーされます。一般的な操作を検出するで説明したように、これらのイベントはすべて onTouchEvent()
の MotionEvent
パラメータに記録されます。
指によるタップは必ずしも最も正確なインタラクション形式とは限らないため、タッチイベントの検出は、多くの場合、単純な接触よりも動きに基づいて行われます。Android には、動きに基づく操作(スワイプなど)と移動以外の操作(シングルタップなど)を区別できるように、タッチスロップの概念があります。タッチスロップとは、ユーザーのタップが動きに基づく操作として解釈されるまでのタップが移動できる距離(ピクセル単位)です。このトピックについて詳しくは、ViewGroup のタッチイベントを管理するをご覧ください。
アプリのニーズに応じて、ジェスチャーの動きをトラッキングする方法はいくつかあります。以下に例を示します。
- ポインタの開始位置と終了位置(画面上のオブジェクトのポイント A からポイント B への移動など)。
- ポインタの移動方向。X 座標と Y 座標によって決まります。
- 履歴。操作の履歴サイズを確認するには、
MotionEvent
メソッドgetHistorySize()
を呼び出します。モーション イベントのgetHistorical<Value>
メソッドを使用して、各過去のイベントの位置、サイズ、時間、圧力を取得できます。履歴は、タップ描画など、ユーザーの指の跡をレンダリングする場合に役立ちます。詳しくは、MotionEvent
のリファレンスをご覧ください。 - タッチスクリーン上でのポインタの移動速度。
以下の関連リソースもご覧ください。
トラッキング速度
ポインタが移動する距離や方向に基づいて、動きに基づく操作を行うことができます。ただし、多くの場合、速度はジェスチャーの特性のトラッキングや、ジェスチャーが発生したかどうかの判断における決定要因になります。速度の計算を容易にするために、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. It's 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. It's 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 is // 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 is // 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 is // 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 is // 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()
を明示的に呼び出さなくてもビューからキャプチャを削除できます。これは一般に、キャプチャをリクエストするビューを含むビュー階層がフォーカスを失うためです。