原代碼在 onTouchEvent 中僅處理 ACTION_DOWN 與 ACTION_POINTER_DOWN,但 座標獲取方式錯誤:event.getX()/getY() 無參數呼叫永遠返回**第一個指標 (index 0)** 的座標,導致多指觸控時只在第一根手指位置繪製波紋。
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked(); // 使用 getActionMasked() 區分動作類型
int pointerIndex = event.getActionIndex(); // 觸發此事件的具體指標索引
int pointerId = event.getPointerId(pointerIndex); // 該指標的唯一 ID (追蹤用)
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN: {
// 取得**該指標**的座標
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
addPoint((int)x, (int)y, pointerId); // 建議傳入 pointerId 以便追蹤
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL: {
// 手指抬起,可選移除對應 pointerId 的波紋或標記結束
// removePoint(pointerId);
break;
}
case MotionEvent.ACTION_MOVE: {
// 多指移動需遍歷所有有效指標
for (int i = 0; i < event.getPointerCount(); i++) {
int id = event.getPointerId(i);
float x = event.getX(i);
float y = event.getY(i);
// updatePoint(id, x, y); // 若需拖動波紋
}
break;
}
}
return true; // 返回 true 表示消費事件,繼續接收後續 MOVE/UP
}
| 錯誤寫法 | 正確寫法 | 說明 |
|---|---|---|
event.getX() | event.getX(pointerIndex) | 必須指定指標索引,否則永遠取第一根手指 |
switch event.getAction() & MASK | event.getActionMasked() | API 8+ 建議用獨立方法,更清晰 |
未使用 pointerId | 儲存 pointerId 對應波紋物件 | 索引可能因手指增減而變動,ID 才是穩定識別碼 |
未處理 ACTION_MOVE | 遍歷 getPointerCount() 更新所有點 | 支援拖動效果的必要邏輯 |
將 ArrayList<myWave> 改為 SparseArray<myWave> 或 HashMap<Integer, myWave>,Key 為 pointerId,Value 為波紋物件,實現 O(1) 增刪查,避免陣列移除導致索引錯亂。
ACTION_DOWN / POINTER_DOWN → 取得 pointerIndex → getX(pointerIndex) → 建立波紋物件存入 Map(key=pointerId) → invalidate() 觸發重繪。ACTION_MOVE → 遍歷所有 pointerIndex → 更新 Map 中對應波紋座標 → invalidate()。ACTION_UP / POINTER_UP → 從 Map 移除該 pointerId → (可選) 啟動淡出動畫 → invalidate()。掌握 ActionMasked + ActionIndex + PointerId + getX(index) 這組「四大金剛」,即可輕鬆實作任意多點觸控互動效果。