(轉)onTouchEvent方法的使用,ontouchevent方法
手機螢幕事件的處理方法onTouchEvent。該方法在View類中的定義,並且所有的View子類全部重寫了該方法,應用程式可以通過該方法處理手機螢幕的觸摸事件。該方法的簽名如下所示。
public boolean onTouchEvent(MotionEvent event)
參數event:參數event為手機螢幕觸摸事件封裝類的對象,其中封裝了該事件的所有資訊,例如觸摸的位置、觸摸的類型以及觸摸的時間等。該對象會在使用者觸摸手機螢幕時被建立。
傳回值:該方法的傳回值機理與鍵盤響應事件的相同,同樣是當已經完整地處理了該事件且不希望其他回調方法再次處理時返回true,否則返回false。
該方法並不像之前介紹過的方法只處理一種事件,一般情況下以下三種情況的事件全部由onTouchEvent方法處理,只是三種情況中的動作值不同。
螢幕被按下:當螢幕被按下時,會自動調用該方法來處理事件,此時MotionEvent.getAction()的值為MotionEvent.ACTION_DOWN,如果在應用程式中需要處理螢幕被按下的事件,只需重新該回調方法,然後在方法中進行動作的判斷即可。
螢幕被抬起:當觸控筆離開螢幕時觸發的事件,該事件同樣需要onTouchEvent方法來捕捉,然後在方法中進行動作判斷。當MotionEvent.getAction()的值為MotionEvent.ACTION_UP時,表示是螢幕被抬起的事件。
在螢幕中拖動:該方法還負責處理觸控筆在螢幕上滑動的事件,同樣是調用MotionEvent.getAction()方法來判斷動作值是否為MotionEvent.ACTION_MOVE再進行處理。
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舉例
1 public class MyGesture extends Activity implements OnTouchListener { 2 public void onCreate(Bundle savedInstanceState) { 3 super.onCreate(savedInstanceState); 4 5 setContentView(R.layout.main); 6 7 TextView tv = (TextView) findViewById(R.id.tv); 8 9 tv.setOnTouchListener(this);10 }11 12 public boolean onTouch(View v, MotionEvent event)13 14 {15 Toast.makeText(this, "Touch Touch", Toast.LENGTH_SHORT).show();16 17 return false;18 }19 20 }
我們可以通過MotionEvent的getAction()方法來擷取Touch事件的類型,包括 ACTION_DOWN(按下觸控螢幕), ACTION_MOVE(按下觸控螢幕後移動受力點), ACTION_UP(鬆開觸控螢幕)和ACTION_CANCEL(不會由使用者直接觸發)。藉助對於使用者不同操作的判斷,結合getRawX()、 getRawY()、getX()和getY()等方法來擷取座標後,我們可以實現諸如拖動某一個按鈕,拖動捲軸等功能。2. 當我們捕捉到Touch操作的時候,如何識別出使用者的Gesture?這裡我們需要GestureDetector.OnGestureListener介面的協助,代碼如下:
1 public class MyGesture extends Activity implements OnTouchListener, OnGestureListener 2 3 { 4 5 private GestureDetector mGestureDetector; 6 7 public MyGesture() 8 9 { 10 11 mGestureDetector = new GestureDetector(this); 12 13 } 14 15 public void onCreate(Bundle savedInstanceState) 16 17 { 18 19 super.onCreate(savedInstanceState); 20 21 setContentView(R.layout.main); 22 23 TextView tv = (TextView) findViewById(R.id.tv); 24 25 tv.setOnTouchListener(this); 26 27 tv.setFocusable(true); 28 29 tv.setClickable(true); 30 31 tv.setLongClickable(true); 32 33 mGestureDetector.setIsLongpressEnabled(true); 34 35 } 36 37 /* 38 * *在onTouch()方法中,我們調用GestureDetector的onTouchEvent()方法, 39 * 將捕捉到的MotionEvent交給GestureDetector * 來分析是否有合適的callback函數來處理使用者的手勢 40 */ 41 42 public boolean onTouch(View v, MotionEvent event) 43 44 { 45 46 return mGestureDetector.onTouchEvent(event); 47 48 } 49 50 // 使用者輕觸觸控螢幕,由1個MotionEvent ACTION_DOWN觸發 51 52 public boolean onDown(MotionEvent arg0) 53 54 { 55 56 Log.i("MyGesture", "onDown"); 57 58 Toast.makeText(this, "onDown", Toast.LENGTH_SHORT).show(); 59 60 return true; 61 62 } 63 64 /* 65 * * 使用者輕觸觸控螢幕,尚未鬆開或拖動,由一個1個MotionEvent ACTION_DOWN觸發 * 66 * 注意和onDown()的區別,強調的是沒有鬆開或者拖動的狀態 67 */ 68 69 public void onShowPress(MotionEvent e) 70 71 { 72 73 Log.i("MyGesture", "onShowPress"); 74 75 Toast.makeText(this, "onShowPress", Toast.LENGTH_SHORT).show(); 76 77 } 78 79 // 使用者(輕觸觸控螢幕後)鬆開,由一個1個MotionEvent ACTION_UP觸發 80 81 public boolean onSingleTapUp(MotionEvent e) 82 83 { 84 85 Log.i("MyGesture", "onSingleTapUp"); 86 87 Toast.makeText(this, "onSingleTapUp", Toast.LENGTH_SHORT).show(); 88 89 return true; 90 91 } 92 93 // 使用者按下觸控螢幕、快速移動後鬆開,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE, 1個ACTION_UP觸發 94 95 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 96 97 { 98 99 Log.i("MyGesture", "onFling");100 101 Toast.makeText(this, "onFling", Toast.LENGTH_LONG).show();102 103 return true;104 105 }106 107 // 使用者按下觸控螢幕,並拖動,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE觸發108 109 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)110 111 {112 113 Log.i("MyGesture", "onScroll");114 115 Toast.makeText(this, "onScroll", Toast.LENGTH_LONG).show();116 117 return true;118 119 }120 121 // 使用者長按觸控螢幕,由多個MotionEvent ACTION_DOWN觸發122 123 public void onLongPress(MotionEvent e)124 125 {126 127 Log.i("MyGesture", "onLongPress");128 129 Toast.makeText(this, "onLongPress", Toast.LENGTH_LONG).show();130 131 }132 133 }
3. Fling事件的處理代碼:除了第一個觸發Fling的ACTION_DOWN和最後一個ACTION_MOVE中包含的座標等資訊外,我們還可以根據用 戶在X軸或者Y軸上的移動速度作為條件。比如下面的代碼中我們就在使用者移動超過100個像素,且X軸上每秒的移動速度大於200像素時才進行處理。
1 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 2 float velocityY) 3 4 { 5 6 // 參數解釋: 7 8 // e1:第1個ACTION_DOWN MotionEvent 9 10 // e2:最後一個ACTION_MOVE MotionEvent11 12 // velocityX:X軸上的移動速度,像素/秒13 14 // velocityY:Y軸上的移動速度,像素/秒15 16 // 觸發條件 :17 18 // X軸的座標位移大於FLING_MIN_DISTANCE,且移動速度大於FLING_MIN_VELOCITY個像素/秒19 20 final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200;21 22 if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE23 && Math.abs(velocityX) > FLING_MIN_VELOCITY)24 25 {26 27 // Fling left28 29 Log.i("MyGesture", "Fling left");30 31 Toast.makeText(this, "Fling Left", Toast.LENGTH_SHORT).show();32 33 }34 35 else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE36 && Math.abs(velocityX) > FLING_MIN_VELOCITY)37 38 {39 40 // Fling right41 42 Log.i("MyGesture", "Fling right");43 44 Toast.makeText(this, "Fling Right", Toast.LENGTH_SHORT).show();45 46 }47 48 return false;49 50 }
這個例子中,tv.setLongClickable(true)是必須的,因為 只有這樣,view才能夠處理不同於Tap(輕觸)的hold(即ACTION_MOVE,或者多個ACTION_DOWN),我們同樣可以通過layout定義中的android:longClickable來做到這一點.