標籤:
原文地址:http://android.xsoftlab.net/training/gestures/index.html
引言
這節課將會學習如何讓使用者通過觸摸手勢與APP產生互動。Android提供了許多相關API來協助你建立、檢測手勢。
儘管APP不應該將觸摸手勢作為基本的輸入特性,但是觸摸手勢可以使APP快速提高可操作性與吸引力。
為了提供一種一貫的,直觀的使用者體驗,APP應當使用Android通用的觸摸手勢標準。
檢測通用手勢
當使用者將一根或者多根手指放置在觸控螢幕上時就會產生觸摸事件,應用程式需要將這次的觸摸行為解釋為一種特別的手勢事件。下面是檢測手勢事件相應的執行步驟:
- 1.收集觸摸事件的相關資料。
- 2.解釋這些資料,查看是否有程式所支援的任何標準手勢。
收集資料
當使用者將手指放在螢幕上時,這會回調相應View的onTouchEvent()方法。
手勢開始於使用者第一次觸到螢幕時,接下來系統會追蹤手指的位置,最後結束於手指離開螢幕時的最後一次事件。在這整個互動過程中,MotionEvent對象由onTouchEvent()方法分發,並提供了每個事件的相關詳細資料。APP可以使用MotionEvent對象所提供的資料來檢查是否有APP所關心的事件發生。
為Activity或View捕獲觸摸事件
為了攔截Activity或者View的觸摸事件,需要重寫它們的onTouchEvent()回調方法。
下面的代碼使用getActionMasked()方法來提取event參數中含有的使用者執行行為。它提供了你所關心的未經處理資料:
public class MainActivity extends Activity {...// This example shows an Activity, but you would use the same approach if// you were subclassing a View.@Overridepublic 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); } }
使單個View捕獲觸摸事件
除了onTouchEvent()方法之外,你還可以使用View.OnTouchListener來監聽觸摸手勢。這使得不重寫onTouchEvent()還可以監聽觸摸事件成為了可能:
View myView = findViewById(R.id.my_view); myView.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { // ... Respond to touch events return true; }});
要注意所建立的監聽器在ACTION_DOWN事件時返回的false。如果你這麼做了,那麼監聽器接下來對於ACTION_MOVE及ACTION_UP等一系列事件將不會調用。這是因為ACTION_DOWN事件是所有觸摸事件的起點。
如果你建立了一個自訂View,你可以像上面描述的那樣重寫onTouchEvent()方法。
檢測手勢
Android提供了GestureDetector類來檢測通用手勢。這些手勢包括onDown(), onLongPress(), onFling()等等。你可以將GestureDetector與onTouchEvent()結合使用。
檢查所有支援的手勢
當你在執行個體化GestureDetectorCompat對象時,其中一個參數需要實現GestureDetector.OnGestureListener介面。
GestureDetector.OnGestureListener介面的作用是:在指定的觸摸事件發生時通知使用者。為了使GestureDetector對象可以接收到觸摸事件,你需要重寫View或者Activity的onTouchEvent()方法,並將所有的事件傳遞給GestureDetector對象。
在下面的代碼中,由onTouchEvent()方法所返回的true代表了你要負責處理這次的觸摸事件。傳回值false則代表你想忽略這次事件,直到這次的觸摸事件被處理完畢。
運行下面的代碼找找感覺:當你在觸控螢幕上操作時每種行為是如何被觸發的;以及每一種觸摸事件的MotionEvent對象的內容是什麼。你將會意識到一個簡單的觸摸事件是由多麼龐大的資料處理產生的。
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; }}
檢測支援手勢的子集
如果你只是想處理幾種手勢,那麼你可以繼承GestureDetector.SimpleOnGestureListener介面。
GestureDetector.SimpleOnGestureListener提供了對於onTouchEvent()方法所有的實現,這樣你只用重寫你所關心的方法。比如說,在下面的代碼中建立了一個繼承GestureDetector.SimpleOnGestureListener介面的類,然後重寫了它的onFling()方法及onDown()方法。
無論你是否使用了GestureDetector.OnGestureListener介面,最佳的練習點在於重寫了返回true的onDown()方法。這是因為所有的手勢都是從onDown()開始的。如果在onDown()方法中返回了false,就像GestureDetector.SimpleOnGestureListener預設做的那樣,那麼系統會認為你想忽略餘下的手勢,並且GestureDetector.OnGestureListener介面的其它方法都不會被調用。這會在APP內埋下一個潛在的不易察覺的問題。如果你確認你要忽略整個手勢流,那麼onDown()中的結果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; } }}
Android官方開發文檔Training系列課程中文版:手勢處理之監測通用手勢