首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Android 手勢檢測 GestureDetector 完整實作指南

     編輯:Java 相關知識

前言

原標題「二進制中 1 的個數」與內容完全不符,實際為 Android GestureDetector 手勢識別代碼。本文整理完整手勢監聽器實作、常見手勢類型、觸發時機與最佳實踐。

核心手勢類型與觸發時機

手勢方法觸發條件典型用途
單擊onSingleTapUp按下→快速抬起,非雙擊項目選中、按鈕點擊
雙擊onDoubleTap兩次快速單擊縮放、進入詳情
長按onLongPress按下不動超過閾值 (~500ms)顯示選單、編輯模式
滑動onScroll按下移動拖拽、畫布平移
快滑onFling快速滑動並抬起翻頁、刪除列表項
按下確認onShowPress按下短暫停留,未移動/抬起視覺反饋 (ripple)
雙擊事件onDoubleTapEvent雙擊期間的 DOWN/MOVE/UP雙擊縮放過程處理

完整手勢監聽器實作

public class GestureHandler extends GestureDetector.SimpleOnGestureListener {
    private final Context context;
    private static final String TAG = "GestureHandler";

    public GestureHandler(Context context) {
        this.context = context;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        // **必須返回 true**,否則後續手勢 (scroll/fling/longpress) 不會觸發
        Log.d(TAG, "onDown: " + e.getAction());
        return true;
    }

    @Override
    public void onShowPress(MotionEvent e) {
        Log.d(TAG, "onShowPress: 短暫按下");
        // 視覺反饋,如高亮項目
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        Log.d(TAG, "onSingleTapUp: 單擊確認");
        // 處理單擊邏輯
        return true; // 消費事件
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        Log.d(TAG, "onDoubleTap: 雙擊");
        // 處理雙擊邏輯 (如縮放)
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        // 雙擊手勢內部的每個事件 (DOWN, MOVE, UP)
        return super.onDoubleTapEvent(e);
    }

    @Override
    public void onLongPress(MotionEvent e) {
        Log.d(TAG, "onLongPress: 長按");
        Toast.makeText(context, "長按觸發", Toast.LENGTH_SHORT).show();
        // 顯示上下文選單、進入編輯模式
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, 
                            float distanceX, float distanceY) {
        Log.d(TAG, "onScroll: 滑動 dx=" + distanceX + " dy=" + distanceY);
        // e1: 首次 DOWN, e2: 當前 MOVE
        // distanceX/Y: 自上次 scroll 移動距離 (正值=向左/向上)
        // 處理拖拽、平移
        return true;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, 
                           float velocityX, float velocityY) {
        Log.d(TAG, "onFling: 快滑 vx=" + velocityX + " vy=" + velocityY);
        // velocityX/Y: 每秒像素速度
        // 判斷方向:Math.abs(velocityX) > Math.abs(velocityY) ? 水平 : 垂直
        // 處理翻頁、刷新、刪除
        return true;
    }
}

Activity/View 整合

public class MainActivity extends AppCompatActivity {
    private GestureDetector gestureDetector;
    private GestureHandler gestureHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        gestureHandler = new GestureHandler(this);
        gestureDetector = new GestureDetector(this, gestureHandler);
        
        // 可選:設定雙擊縮放等參數
        // gestureDetector.setIsLongpressEnabled(true); // 預設 true
    }

    /** 核心:將觸摸事件委派給 GestureDetector */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 先讓 GestureDetector 處理,返回 true 表示已消費
        boolean consumed = gestureDetector.onTouchEvent(event);
        if (consumed) return true;
        
        // 未消費則交給父類 (如滑動衝突處理)
        return super.onTouchEvent(event);
    }
    
    // 若在特定 View 上處理
    public void setupViewTouch(View targetView) {
        targetView.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
    }
}

進階:ScaleGestureDetector 雙指縮放

private ScaleGestureDetector scaleDetector;

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...
    scaleDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scaleFactor = detector.getScaleFactor(); // >1 放大, <1 縮小
            // 更新縮放矩陣、重繪
            return true;
        }
    });
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    scaleDetector.onTouchEvent(event); // 先處理雙指
    return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
}

常見坑點與解決

問題原因解決
onScroll/onFling 不觸發onDown 返回 falseonDown **必須返回 true**
長按與滑動衝突長按閾值觸發前移動會取消長按調整邏輯或用 onShowPress 給視覺提示
ListView/RecyclerView 滑動衝突父容器攔截觸摸事件請求父容器 requestDisallowInterceptTouchEvent(true)
雙擊與單擊衝突單擊需等待雙擊超時才觸發單擊邏輯放 onSingleTapConfirmed (API 16+)
座標系統混淆getX()/getRawX()、distanceX 正負統一用相對座標,distanceX 正=向左移動

總結

  1. 繼承 SimpleOnGestureListener,僅覆寫需要的手勢
  2. onDown 必須返回 true 是所有手勢生效的前提
  3. 在 Activity/View 的 onTouchEvent 中委派 gestureDetector.onTouchEvent(event)
  4. 複雜場景組合 ScaleGestureDetector (雙指縮放)、ViewDragHelper (拖拽)
  5. 注意觸摸事件分發鏈:dispatchTouchEvent → onInterceptTouchEvent → onTouchEvent
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved