Android 觸控手勢識別分為兩類:觸摸屏手勢(API 1+)與輸入法手勢(API 4+)。本文聚焦前者,透過 GestureDetector 實作翻頁、縮放、長按等交互。
GestureDetector:手勢識別引擎OnGestureListener:基礎手勢回調(6 方法)OnDoubleTapListener:雙擊相關回調(3 方法)SimpleOnGestureListener:空實現適配器,按需覆寫| 手勢動作 | 觸發回調序列 | 關鍵參數 |
|---|---|---|
| 單擊 | onDown → onSingleTapUp | e: DOWN 事件 |
| 長按 | onDown → onShowPress → onLongPress | 需 setIsLongpressEnabled(true) |
| 滑動 | onDown → onScroll(多次) | distanceX/Y: 像素位移 |
| 快速滑動 | onDown → onScroll... → onFling | velocityX/Y: 像素/秒 |
| 雙擊 | onDown → onSingleTapUp → onDoubleTap | 需 OnDoubleTapListener |
public class GestureSurfaceView extends SurfaceView
implements SurfaceHolder.Callback, Runnable,
GestureDetector.OnGestureListener, View.OnTouchListener {
private GestureDetector detector;
private Bitmap currentBmp;
private final Vector<String> gestureLog = new Vector<>();
private boolean toggle = false;
public GestureSurfaceView(Context context) {
super(context);
// 初始化繪圖資源
currentBmp = BitmapFactory.decodeResource(getResources(), R.drawable.img1);
// 手勢檢測器
detector = new GestureDetector(context, this);
detector.setIsLongpressEnabled(true); // 啟用長按
// 觸控監聽
setOnTouchListener(this);
setLongClickable(true); // 關鍵:允許處理 MOVE/長按等事件
getHolder().addCallback(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureLog.clear();
return detector.onTouchEvent(event); // 委派給手勢檢測器
}
// === OnGestureListener 實作 ===
@Override public boolean onDown(MotionEvent e) {
log("onDown"); return false;
}
@Override public void onShowPress(MotionEvent e) {
log("onShowPress");
}
@Override public void onLongPress(MotionEvent e) {
log("onLongPress");
}
@Override public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
log("onScroll: dx=" + distanceX + ", dy=" + distanceY);
return false;
}
@Override public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
log("onFling: vx=" + velocityX + ", vy=" + velocityY);
// 翻頁邏輯:水平速度超過閾值
if (Math.abs(velocityX) > 1000) {
toggle = !toggle;
currentBmp = BitmapFactory.decodeResource(getResources(),
toggle ? R.drawable.img2 : R.drawable.img1);
}
return false;
}
@Override public boolean onSingleTapUp(MotionEvent e) {
log("onSingleTapUp"); return false;
}
private void log(String msg) {
gestureLog.add(msg);
}
// 繪製迴圈略...
}
官方文檔常誤導為 DOWN/MOVE,實測:
e1:首個 DOWN 事件e2:最終 UP 事件因此 e2.getX() - e1.getX() 即為完整滑動水平位移。
手指未離開螢幕時,無論速度多快,只會觸發 onScroll。onFling 僅在 ACTION_UP 瞬間觸發,適合「翻頁、清單快速滑動」判斷。
// 正確鏈路
setOnTouchListener(this)
→ onTouch()
→ detector.onTouchEvent(event)
→ OnGestureListener 回調
若同時覆寫 onTouchEvent() 且未返回 true,事件會繼續傳遞導致重複處理。
若內建手勢不足,可用 GestureOverlayView + GestureLibrary 實作「輸入法手勢」(API 4+):
gestures 文件gestureOverlay.addOnGesturePerformedListener()library.recognize(gesture) 取得預測結果GestureDetector 封裝了複雜的觸控狀態機,掌握「DOWN→SCROLL/FLING→UP」時序與參數含義,即可實作流暢的手勢交互。記住:長按需啟用、翻頁用 Fling、滑動用 Scroll、單擊用 SingleTapUp。