Android 檢測(cè)常用的手勢(shì)

2018-08-02 18:20 更新

編寫(xiě):Andrwyw - 原文:http://developer.android.com/training/gestures/detector.html

當(dāng)用戶(hù)把用一根或多根手指放在觸摸屏上,并且應(yīng)用把這樣的觸摸方式解釋為特定的手勢(shì)時(shí),“觸摸手勢(shì)”就發(fā)生了。相應(yīng)地,檢測(cè)手勢(shì)也就有以下兩個(gè)階段:

  1. 收集觸摸事件的相關(guān)數(shù)據(jù)。
  2. 分析這些數(shù)據(jù),看它們是否符合app所支持的手勢(shì)的標(biāo)準(zhǔn)。

Support Library 中的類(lèi)

本節(jié)課程的示例程序使用了GestureDetectorCompatMotionEventCompat類(lèi)。這些類(lèi)都是在 Support Library 中定義的。如果有可能的情況話,我們應(yīng)該使用 Support Library 中的類(lèi),來(lái)為運(yùn)行著Android1.6及以上版本系統(tǒng)的設(shè)備提供兼容性功能。需要注意的一點(diǎn)是,MotionEventCompat并不是MotionEvent的替代品,而是提供了一些靜態(tài)工具類(lèi)函數(shù)。我們可以把MotionEvent對(duì)象作為參數(shù)傳遞給這些工具類(lèi)函數(shù),來(lái)獲得與觸摸事件相關(guān)的動(dòng)作(action)。

收集數(shù)據(jù)

當(dāng)用戶(hù)把用一根或多根手指放在觸摸屏上時(shí),會(huì)觸發(fā) View 上用于接收觸摸事件的 onTouchEvent() 回調(diào)函數(shù)。對(duì)于一系列連續(xù)的、最終會(huì)被識(shí)別為一種手勢(shì)的觸摸事件(位置、壓力、大小、添加另一根手指等等),onTouchEvent()會(huì)被調(diào)用若干次。

當(dāng)用戶(hù)第一次觸摸屏幕時(shí),手勢(shì)就開(kāi)始了。其后系統(tǒng)會(huì)持續(xù)地追蹤用戶(hù)手指的位置,在用戶(hù)手指全都離開(kāi)屏幕時(shí),手勢(shì)結(jié)束。在整個(gè)交互期間,被分發(fā)給 onTouchEvent() 函數(shù)的 MotionEvent 對(duì)象,提供了每次交互的詳細(xì)信息。我們的app可以使用 MotionEvent 提供的這些數(shù)據(jù),來(lái)判斷某種特定的手勢(shì)是否發(fā)生了。

為Activity或View捕獲觸摸事件

為了捕獲Activity或View中的觸摸事件,我們可以重寫(xiě)onTouchEvent()回調(diào)函數(shù)。

接下來(lái)的代碼段使用了getActionMasked()函數(shù),來(lái)從 event 參數(shù)中抽取出用戶(hù)執(zhí)行的動(dòng)作。它提供了一些原始的觸摸數(shù)據(jù),我們可以使用這些數(shù)據(jù),來(lái)判斷某個(gè)特定手勢(shì)是否發(fā)生了。

public class MainActivity extends Activity {
...
// This example shows an Activity, but you would use the same approach if
// you were subclassing a View.
@Override
public boolean onTouchEvent(MotionEvent event){

        int action = MotionEventCompat.getActionMasked(event);

        switch(action) {
                case (MotionEvent.ACTION_DOWN) :
                Log.d(DEBUG_TAG,"Action was DOWN");
                return true;
        case (MotionEvent.ACTION_MOVE) :
                Log.d(DEBUG_TAG,"Action was MOVE");
                return true;
        case (MotionEvent.ACTION_UP) :
                Log.d(DEBUG_TAG,"Action was UP");
                return true;
        case (MotionEvent.ACTION_CANCEL) :
                Log.d(DEBUG_TAG,"Action was CANCEL");
                return true;
        case (MotionEvent.ACTION_OUTSIDE) :
                Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
                        "of current screen element");
                return true;
        default :
                return super.onTouchEvent(event);
        }
}

然后,我們可以對(duì)這些事件做些自己的處理,以判斷某個(gè)手勢(shì)是否出現(xiàn)了。這種是針對(duì)自定義手勢(shì),我們所需要進(jìn)行的處理。然而,如果我們的app僅僅需要一些常見(jiàn)的手勢(shì),如雙擊,長(zhǎng)按,快速滑動(dòng)(fling)等,那么我們可以使用GestureDetector類(lèi)來(lái)完成。 GestureDetector可以讓我們簡(jiǎn)單地檢測(cè)常見(jiàn)手勢(shì),并且無(wú)需自行處理單個(gè)觸摸事件。相關(guān)內(nèi)容將會(huì)在下面的檢測(cè)手勢(shì)中討論。

捕獲單個(gè)view的觸摸事件

作為onTouchEvent()的一種替換方式,我們也可以使用 setOnTouchListener() 函數(shù),來(lái)把 View.OnTouchListener 關(guān)聯(lián)到任意的View上。這樣可以在不繼承已有的 View 的情況下,也能監(jiān)聽(tīng)觸摸事件。比如:

View myView = findViewById(R.id.my_view);
myView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
        // ... Respond to touch events
        return true;
    }
});

創(chuàng)建listener對(duì)象時(shí),注意 ACTION_DOWN 事件返回 false 的情況。如果返回 false,會(huì)讓listener對(duì)象接收不到后續(xù)的ACTION_MOVEACTION_UP等系列事件。這是因?yàn)?a rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" target="_blank">ACTION_DOWN事件是所有觸摸事件的開(kāi)端。

如果我們正在寫(xiě)一個(gè)自定義View,我們也可以像上面描述的那樣重寫(xiě)onTouchEvent()函數(shù)。

檢測(cè)手勢(shì)

Android提供了GestureDetector類(lèi)來(lái)檢測(cè)常用的手勢(shì)。它所支持的手勢(shì)包括onDown()、onLongPress()、onFling() 等。我們可以把GestureDetector和上面描述的onTouchEvent()函數(shù)結(jié)合在一起使用。

檢測(cè)所有支持的手勢(shì)

當(dāng)我們實(shí)例化一個(gè)GestureDetectorCompat對(duì)象時(shí),需要一個(gè)實(shí)現(xiàn)了GestureDetector.OnGestureListener接口的類(lèi)作為參數(shù)。當(dāng)某個(gè)特定的觸摸事件發(fā)生時(shí),GestureDetector.OnGestureListener就會(huì)通知用戶(hù)。為了讓我們的GestureDetector對(duì)象能到接收到觸摸事件,我們需要重寫(xiě) View 或 Activity 的 onTouchEvent() 函數(shù),并且把所有捕獲到的事件傳遞給 detector 實(shí)例。

接下來(lái)的代碼段中,on<TouchEvent> 型的函數(shù)的返回值是 true,意味著我們已經(jīng)處理完這個(gè)觸摸事件了。如果返回 false,則會(huì)把事件沿view棧傳遞,直到觸摸事件被成功地處理了。

運(yùn)行下面的代碼段,來(lái)了解當(dāng)我們與觸摸屏交互時(shí),動(dòng)作(action)是如何觸發(fā)的,以及每個(gè)觸摸事件MotionEvent中的內(nèi)容。我們也會(huì)意識(shí)到,一個(gè)簡(jiǎn)單的交互會(huì)產(chǎn)生多少的數(shù)據(jù)。

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;
    }
}

檢測(cè)部分支持的手勢(shì)

如果我們只想處理幾種手勢(shì),那么可以選擇繼承 GestureDetector.SimpleOnGestureListener 類(lèi),而不是實(shí)現(xiàn) GestureDetector.OnGestureListener 接口。

GestureDetector.SimpleOnGestureListener 類(lèi)實(shí)現(xiàn)了所有的 on<TouchEvent> 型函數(shù),其中,這些函數(shù)都返回 false。因此,我們可以?xún)H僅重寫(xiě)我們需要的函數(shù)。比如,下面的代碼段中,創(chuàng)建了一個(gè)繼承自 GestureDetector.SimpleOnGestureListener 的類(lèi),并重寫(xiě)了 onFling() 和 onDown() 函數(shù)。

無(wú)論我們是否使用GestureDetector.OnGestureListener類(lèi),最好都實(shí)現(xiàn) onDown() 函數(shù)并且返回 true。這是因?yàn)樗械氖謩?shì)都是由 onDown() 消息開(kāi)始的。如果讓 onDown() 函數(shù)返回 false,就像GestureDetector.SimpleOnGestureListener類(lèi)中默認(rèn)實(shí)現(xiàn)的那樣,系統(tǒng)會(huì)假定我們想忽略剩余的手勢(shì),GestureDetector.OnGestureListener中的其他函數(shù)也就永遠(yuǎn)不會(huì)被調(diào)用。這可能會(huì)導(dǎo)致我們的app出現(xiàn)意想不到的問(wèn)題。僅僅當(dāng)我們真的想忽略全部手勢(shì)時(shí),我們才應(yīng)該讓 onDown() 函數(shù)返回 false

public class MainActivity extends Activity {

    private GestureDetectorCompat mDetector;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDetector = new GestureDetectorCompat(this, new MyGestureListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        this.mDetector.onTouchEvent(event);
        return super.onTouchEvent(event);
    }

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        private static final String DEBUG_TAG = "Gestures";

        @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;
        }
    }
}


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)