Android Touch Screen 與傳統Click Touch Screen不同,會有一些手勢(Gesture),例如Fling,Scroll等等。這些Gesture會使使用者體驗大大提升。
Android中的Gesture識別(detector)是通過GestureDetector.OnGestureListener介面實現的。
首先,Android事件處理機制是基於Listener實現的,比如觸控螢幕相關的事件,就是通過onTouchListener實現;
其次,所有View的子類都可以通過setOnTouchListener()、setOnKeyListener()等方法來添加對某一類事件的Listener;
第三,Listener一般會以Interface的方式來提供,其中包含一個或多個abstract方法,我們需要實現這些方法來完成 onTouch()、onKey()等操作。這樣,程式便可以在特定的事件被dispatch到該view的時候,通過callback函數給予適當的響 應。
1. Touch Screen Click舉例
public class MyGesture extends Activity implements OnTouchListener
{
public void onCreate(Bundle savedInstanceState)
{
super .onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv = (TextView) findViewById(R.id.tv);
tv.setOnTouchListener( this );
}
public boolean onTouch(View v, MotionEvent event)
{
Toast.makeText( this , "Touch Touch" , Toast.LENGTH_SHORT).show();
return false ;
}
}
我們可以通過MotionEvent的getAction()方法來擷取Touch事件的類型,包括 ACTION_DOWN(按下觸控螢幕), ACTION_MOVE(按下觸控螢幕後移動受力點), ACTION_UP(鬆開觸控螢幕)和ACTION_CANCEL(不會由使用者直接觸發)。藉助對於使用者不同操作的判斷,結合getRawX()、 getRawY()、getX()和getY()等方法來擷取座標後,我們可以實現諸如拖動某一個按鈕,拖動捲軸等功能。
2. 當我們捕捉到Touch操作的時候,如何識別出使用者的Gesture?這裡我們需要GestureDetector.OnGestureListener介面的協助,代碼如下:
public class MyGesture extends Activity implements OnTouchListener, OnGestureListener
{
private GestureDetector mGestureDetector;
public MyGesture()
{
mGestureDetector = new GestureDetector( this );
}
public void onCreate(Bundle savedInstanceState)
{
super .onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv = (TextView) findViewById(R.id.tv);
tv.setOnTouchListener( this );
tv.setFocusable( true );
tv.setClickable( true );
tv.setLongClickable( true );
mGestureDetector.setIsLongpressEnabled( true );
}
/* * 在onTouch()方法中,我們調用GestureDetector的onTouchEvent()方法,將捕捉到的MotionEvent交給GestureDetector * 來分析是否有合適的callback函數來處理使用者的手勢 */
public boolean onTouch(View v, MotionEvent event)
{
return mGestureDetector.onTouchEvent(event);
}
// 使用者輕觸觸控螢幕,由1個MotionEvent ACTION_DOWN觸發
public boolean onDown(MotionEvent arg0)
{
Log.i( "MyGesture" , "onDown" );
Toast.makeText( this , "onDown" , Toast.LENGTH_SHORT).show();
return true ;
}
/* * 使用者輕觸觸控螢幕,尚未鬆開或拖動,由一個1個MotionEvent ACTION_DOWN觸發 * 注意和onDown()的區別,強調的是沒有鬆開或者拖動的狀態 */
public void onShowPress(MotionEvent e)
{
Log.i( "MyGesture" , "onShowPress" );
Toast.makeText( this , "onShowPress" , Toast.LENGTH_SHORT).show();
}
// 使用者(輕觸觸控螢幕後)鬆開,由一個1個MotionEvent ACTION_UP觸發
public boolean onSingleTapUp(MotionEvent e)
{
Log.i( "MyGesture" , "onSingleTapUp" );
Toast.makeText( this , "onSingleTapUp" , Toast.LENGTH_SHORT).show();
return true ;
}
// 使用者按下觸控螢幕、快速移動後鬆開,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE, 1個ACTION_UP觸發
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
Log.i( "MyGesture" , "onFling" );
Toast.makeText( this , "onFling" , Toast.LENGTH_LONG).show();
return true ;
}
// 使用者按下觸控螢幕,並拖動,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE觸發
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{
Log.i( "MyGesture" , "onScroll" );
Toast.makeText( this , "onScroll" , Toast.LENGTH_LONG).show();
return true ;
}
// 使用者長按觸控螢幕,由多個MotionEvent ACTION_DOWN觸發
public void onLongPress(MotionEvent e)
{
Log.i( "MyGesture" , "onLongPress" );
Toast.makeText( this , "onLongPress" , Toast.LENGTH_LONG).show();
}
}
3. Fling事件的處理代碼:除了第一個觸發Fling的ACTION_DOWN和最後一個ACTION_MOVE中包含的座標等資訊外,我們還可以根據用 戶在X軸或者Y軸上的移動速度作為條件。比如下面的代碼中我們就在使用者移動超過100個像素,且X軸上每秒的移動速度大於200像素時才進行處理。
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
// 參數解釋:
// e1:第1個ACTION_DOWN MotionEvent
// e2:最後一個ACTION_MOVE MotionEvent
// velocityX:X軸上的移動速度,像素/秒
// velocityY:Y軸上的移動速度,像素/秒
// 觸發條件 :
// X軸的座標位移大於FLING_MIN_DISTANCE,且移動速度大於FLING_MIN_VELOCITY個像素/秒
final int FLING_MIN_DISTANCE = 100 , FLING_MIN_VELOCITY = 200 ;
if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY)
{
// Fling left
Log.i( "MyGesture" , "Fling left" );
Toast.makeText( this , "Fling Left" , Toast.LENGTH_SHORT).show();
}
else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY)
{
// Fling right
Log.i( "MyGesture" , "Fling right" );
Toast.makeText( this , "Fling Right" , Toast.LENGTH_SHORT).show();
}
return false ;
}
這個例子中,tv.setLongClickable(true)是必須的,因為 只有這樣,view才能夠處理不同於Tap(輕觸)的hold(即ACTION_MOVE,或者多個ACTION_DOWN),我們同樣可以通過layout定義中的android:longClickable來做到這一點。