本課程說明如何追蹤觸控事件中的移動。
每當目前的觸控接觸位置、壓力或大小發生變化時,系統就會觸發新的 onTouchEvent()ACTION_MOVE 事件。如「偵測常用手勢」一節所述,所有這些事件都會記錄在 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; } });
無論您使用自訂檢視區塊或註冊監聽器,檢視區塊都會收到 MotionEvent,其中包含指標座標,可指定 X 或 Y 增量等相對移動,類似於觸控球裝置傳送的座標。您可以使用 getX() 和 getY() 擷取座標。
釋放指標擷取
應用程式中的檢視區塊可以呼叫 releasePointerCapture() 來釋放指標擷取,如下列程式碼範例所示:
Kotlin
override fun onClick(view: View) { view.releasePointerCapture() }
Java
@Override public void onClick(View view) { view.releasePointerCapture(); }
系統可以從檢視區塊中移除擷取畫面,您不必明確呼叫 releasePointerCapture(),通常是因為包含要求擷取畫面的檢視區塊的檢視區塊階層失去焦點。