このレッスンでは、タッチイベントの動きをトラッキングする方法について説明します。
現在のタッチ接触位置、圧力、サイズが変わるたびに、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()
を明示的に呼び出すことなく、システムはビューからキャプチャを取り除くことができます。これは通常、キャプチャをリクエストするビューを含むビュー階層がフォーカスを失ったためです。