Android 手勢識別——單擊/雙擊


為什么需要手勢識別?

      手勢對於我們的app有很多的地方都在使用,比如右滑關閉界面等。手勢控制分為觸發動作(Touch Mechanics,用戶手指在屏幕上如何動作)和觸發行為(Touch Activities,界面上特定動作在特定情境下引發的結果)。這是因為同樣的觸發動作(如單次觸擊)在不同情境下可能會帶來不同的結果(如輕觸,取消,開啟/關閉指示),同樣單次觸發行為(如放大)可能是由多種觸發動作(如捏放,雙次觸擊,雙次觸擊拖拽等)實現。

     一般情況下,我們知道View類有個View.OnTouchListener內部接口,通過重寫他的onTouch(View v, MotionEvent event)方法,我們可以處理一些touch事件,但是這個方法太過簡單,如果需要處理一些復雜的手勢,用這個接口就會很麻煩(因為我們要自己根據用戶觸摸的軌跡去判斷是什么手勢)。

     Android sdk給我們提供了GestureDetector類,通過這個類我們可以識別很多的手勢,主要是通過他的onTouchEvent(event)方法完成了不同手勢的識別。雖然他能識別手勢,但是不同的手勢要怎么處理,應該是提供給程序員實現的。

GestureDetector這個類對外提供了兩個接口和一個外部類,用於監聽不同的手勢
接口: OnGestureListener, OnDoubleTapListener
內部類: SimpleOnGestureListener

1、實現 GestureDetector.OnGestureListener

private class gesturelistener implements GestureDetector.OnGestureListener{

    public boolean onDown(MotionEvent e) {
        // TODO Auto-generated method stub  
        return false;
    }

    public void onShowPress(MotionEvent e) {
        // TODO Auto-generated method stub  

    }

    public boolean onSingleTapUp(MotionEvent e) {
        // TODO Auto-generated method stub  
        return false;
    }

    public boolean onScroll(MotionEvent e1, MotionEvent e2,
            float distanceX, float distanceY) {
        // TODO Auto-generated method stub  
        return false;
    }

    public void onLongPress(MotionEvent e) {
        // TODO Auto-generated method stub  

    }

    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) {
        // TODO Auto-generated method stub  
        return false;
    }

}

實現該接口時,需要重載六個函數。 下面講解一下這六個函數的觸發條件(具體參看源碼中的注釋):

  • OnDown(MotionEvent e):用戶按下屏幕就會觸發;
  • onShowPress(MotionEvent e):如果是按下的時間超過瞬間,而且在按下的時候沒有松開或者是拖動的,那么onShowPress就會執行。官方的注釋如下:
 /**
         * The user has performed a down {@link MotionEvent} and not performed
         * a move or up yet. This event is commonly used to provide visual
         * feedback to the user to let them know that their action has been
         * recognized i.e. highlight an element.
         *
         * @param e The down motion event
         */
  • onLongPress(MotionEvent e):長按觸摸屏,超過一定時長,就會觸發這個事件
    觸發順序:
        onDown->onShowPress->onLongPress
  • onSingleTapUp(MotionEvent e):從名子也可以看出,一次單獨的輕擊抬起操作,也就是輕擊一下屏幕,立刻抬起來,才會有這個觸發,當然,如果除了Down以外還有其它操作,那就不再算是Single操作了,所以也就不會觸發這個事件
        觸發順序:
        點擊一下非常快的(不滑動)Touchup:
        onDown->onSingleTapUp->onSingleTapConfirmed
        點擊一下稍微慢點的(不滑動)Touchup:
        onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
  • onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) :滑屏,用戶按下觸摸屏、快速移動后松開,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE, 1個ACTION_UP觸發  
    參數解釋:(當滑動屏幕的速度較慢時,該方法不會被調用)
        e1:第1個ACTION_DOWN MotionEvent
        e2:最后一個ACTION_MOVE MotionEvent
        velocityX:X軸上的移動速度,像素/秒
        velocityY:Y軸上的移動速度,像素/秒  

注: 可以通過該函數里的滑動距離和滑動速度,判斷是左滑還是右滑

  • onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY):在屏幕上拖動事件。無論是用手拖動view,或者是以拋的動作滾動,都會多次觸發,這個方法       在ACTION_MOVE動作發生時就會觸發
        滑屏:手指觸動屏幕后,稍微滑動后立即松開
        onDown-----》onScroll----》onScroll----》onScroll----》………----->onFling
        拖動(當滑動的速度不是很快的時候,onFling函數最后不會被調用。)
        onDown------》onScroll----》onScroll

 

2、實現OnDoubleTapListener

/**
* The listener that is used to notify when a double-tap or a confirmed
* single-tap occur.
*/

    OnGestureListener實現了單擊相關的手勢識別,對於雙擊相關的手勢識別需要單獨實現,下面具體介紹里面的幾個方法:

 private GestureDetector.OnDoubleTapListener mOnDoubleTapListener = new GestureDetector.OnDoubleTapListener() {
        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            logger("onSingleTapConfirmed:");
            return false;
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            logger("onDoubleTap:");
            return false;
        }

        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            logger("onDoubleTapEvent:"+MotionEvent.actionToString(e.getAction()));
            return false;
        }
    };

onSingleTapConfirmed(MotionEvent e):單擊事件。用來判定該次點擊是SingleTap而不是DoubleTap,如果連續點擊兩次就是DoubleTap手勢,如果只點擊一次,系統等待一段時間后沒有收到第二次點擊則判定該次點擊為SingleTap而不是DoubleTap,然后觸發SingleTapConfirmed事件。觸發順序是:OnDown->OnsingleTapUp->OnsingleTapConfirmed
關於onSingleTapConfirmed和onSingleTapUp的一點區別: OnGestureListener有這樣的一個方法onSingleTapUp,和onSingleTapConfirmed容易混淆。二者的區別是:onSingleTapUp,只要手抬起就會執行,而對於onSingleTapConfirmed來說,如果雙擊的話,則onSingleTapConfirmed不會執行。

onDoubleTap(MotionEvent e):雙擊事件

onDoubleTapEvent(MotionEvent e):雙擊間隔中發生的動作。指觸發onDoubleTap以后,在雙擊之間發生的其它動作,包含down、up和move事件;下圖是雙擊一下的Log輸出:

3、實現SimpleOnGestureListener

/**
     * A convenience class to extend when you only want to listen for a subset
     * of all the gestures. This implements all methods in the
     * {@link OnGestureListener}, {@link OnDoubleTapListener}, and {@link OnContextClickListener}
     * but does nothing and return {@code false} for all applicable methods.
     */
    public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener,OnContextClickListener{…..}

它與前兩個不同的是:
1、這是一個類,在它基礎上新建類的話,要用extends派生而不是用implements繼承!
2、OnGestureListener和OnDoubleTapListener接口里的函數都是強制必須重寫的,即使用不到也要重寫出來一個空函數但在SimpleOnGestureListener類的實例或派生類中不必如此,可以根據情況,用到哪個函數就重寫哪個函數,因為SimpleOnGestureListener類本身已經實現了這兩個接口的所有函數,只是里面全是空的而已。

4、通過兼容包中工具識別手勢

GestureDetector屬於android.view包,android還提供了android.gesture包支持更多的手勢操作。官方的介紹中使用了GestureDetectorCompat處理手勢識別。 在識別手勢的時候盡量使用 兼容包中手勢識別。

public class MainActivity extends Activity implements
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener{

    private static final String DEBUG_TAG = "Gestures";
    private GestureDetectorCompat mDetector;

    // Called when the activity is first created. 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener
        mDetector = new GestureDetectorCompat(this,this);
        // Set the gesture detector as the double tap
        // listener.
        mDetector.setOnDoubleTapListener(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        this.mDetector.onTouchEvent(event);
        // Be sure to call the superclass implementation
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onDown(MotionEvent event) {
        Log.d(DEBUG_TAG,"onDown: " + event.toString());
        return true;
    }

    @Override
    public boolean onFling(MotionEvent event1, MotionEvent event2,
            float velocityX, float velocityY) {
        Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());
        return true;
    }

    @Override
    public void onLongPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
            float distanceY) {
        Log.d(DEBUG_TAG, "onScroll: " + e1.toString()+e2.toString());
        return true;
    }

    @Override
    public void onShowPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
        return true;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
        return true;
    }
}
View Code

有兩個構造方法:一個構造方法入上述手勢識別的代碼,另一種增加了一個Handler參數,如下:

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM