MotionEvent簡介
當用戶觸摸屏幕時,將創建一個MontionEvent對象,MotionEvent包含了關於發生觸摸的位置、時間信息,以及觸摸事件的其他細節。
獲取MontionEvent對象的方法有:
- 在View或Activity的onTouchEvent方法中: public boolean onTouchEvent(MotionEvent event) {}
- 實現OnTouchListener接口后在onTouch方法中: public boolean onTouch(View v, MotionEvent event) {}
我們可以從一個MotionEvent對象中獲得哪些信息呢?
1、事件的類型可以通過getAction()獲得事件的類型,在android2.2之后加入了對多點觸控的支持,多點觸控時需使用getActionMasked()方法注意: event.getActionMasked() 和 event.getAction() & MotionEvent.ACTION_MASK 是等價的getActionMasked的低8位是動作類型信息(和getAction的值是一樣的), 8-15位是觸控點的索引信息( 即表示是哪一個觸控點的事件)
- ACTION_DOWN: 表示用戶開始觸摸(在第一個點被按下時觸發)
- ACTION_MOVE: 當有點在屏幕上移動時觸發,注意的是,由於靈敏度很高,所以基本上只要有點在屏幕上,此事件就會不停地被觸發
- ACTION_UP:表示用戶抬起了手指(當屏幕上唯一的點被放開時觸發)
- ACTION_CANCEL:表示手勢被取消了
- ACTION_OUTSIDE: 表示用戶觸碰超出了正常的UI邊界
- ACTION_POINTER_DOWN:當屏幕上已經有一個點被按住,此時再按下其他點時觸發
- ACTION_POINTER_UP:當屏幕上有多個點被按住,松開其中一個點時觸發(非最后一個點)
2、事件發生的位置
- getX() 獲得事件發生時,觸摸的中間區域相對view的觸摸位置坐標(不會超過view的長度和寬度)
- getRawX() 和上面getX()不同的是,此方法獲得的是相對屏幕的位置坐標
- getX(int pointerIndex) 在多點觸控中,用來獲取第pointerIndex個觸控點的x位置坐標
3、其他信息
- getPointerCount(); //獲取觸控點的數量,比如2則可能是兩個手指同時按壓屏幕
- getPointerId(nID); //對於每個觸控的點的細節,我們可以通過一個循環執行getPointerId方法獲取索引
- getPressure(nID); //LCD可以感應出用戶的手指壓力,當然具體的級別由驅動和物理硬件決定的
- getDownTime() //按下開始時間
- getEventTime() // 事件結束時間
- getEventTime()-event.getDownTime()); //總共按下時花費時間
- getEdgeFlags():當事件類型是ActionDown時可以通過此方法獲得手指觸控開始的邊界,如果是的話,有如下幾種值:EDGE_LEFT、EDGE_TOP、EDGE_RIGHT、EDGE_BOTTOM
定義的常量
MotionEvent中定義的常量有以下幾個
touch事件:
- ACTION_DOWN = 0;單點觸摸動作
- ACTION_UP = 1;單點觸摸離開動作
- ACTION_MOVE = 2;觸摸點移動動作
- ACTION_CANCEL = 3;觸摸動作取消
- ACTION_OUTSIDE = 4;觸摸動作超出邊界
- ACTION_POINTER_DOWN = 5;多點觸摸動作
- ACTION_POINTER_UP = 6;多點離開動作
其他:
- ACTION_HOVER_MOVE = 7;
- ACTION_SCROLL = 8;
- ACTION_HOVER_ENTER = 9;
- ACTION_HOVER_EXIT = 10;
- ACTION_MASK = 0X000000ff 掩碼常量
- ACTION_POINTER_INDEX_MASK = 0X0000ff00 動作掩碼
- ACTION_POINTER_INDEX_SHIFT = 8 觸摸點索引掩碼,獲取觸摸點索引需要移動的位數
雙指縮放圖片
public class MainActivity extends Activity implements OnTouchListener {
private ImageView myImageView;private int mode = 0; //觸摸點數private float oldDist;private float scale = 1;private float lastScale = 1;//記錄手指全部離開時的縮放比例,下次縮放時是在次基礎上進行的@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);myImageView = new ImageView(this);myImageView.setImageResource(R.drawable.ic_launcher);myImageView.setBackgroundColor(0x8822ffff); //從背景可以看出,此ImageView 是占用整個屏幕大小的myImageView.setScaleType(ScaleType.MATRIX);myImageView.setOnTouchListener(this);setContentView(myImageView);}@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:mode = 1;Log.e("View", "ACTION_DOWN,觸摸點數為1");break;case MotionEvent.ACTION_UP://當屏幕上唯一的點被放開時觸發lastScale = scale;mode = 0;Log.e("View", "ACTION_UP,觸摸點數為0");break;case MotionEvent.ACTION_POINTER_UP://當屏幕上有多個點被按住,松開其中一個點時觸發(即非最后一個點被放開時)mode -= 1;Log.e("View", "ACTION_POINTER_UP,觸摸點數-1");break;case MotionEvent.ACTION_POINTER_DOWN://當屏幕上已經有一個點被按住,此時再按下其他點時觸發oldDist = spacing(event);//第二個點按下時的和第一個點的距離mode += 1;Log.e("View", "ACTION_POINTER_DOWN,觸摸點數+1," + oldDist);break;case MotionEvent.ACTION_MOVE://當有點在屏幕上移動時觸發if (mode >= 2) {float newDist = spacing(event);//移動過程中,第二個點和第一個點的距離if (Math.abs(newDist - oldDist) > 10) {//減小靈敏度scale = lastScale * newDist / oldDist;Log.e("View", "ACTION_MOVE,縮放比例為" + scale);Matrix mMatrix = new Matrix();mMatrix.setScale(scale, scale);myImageView.setImageMatrix(mMatrix);}break;}}return true;//若view的onTouch返回true(只有這樣才能接收事件),那么onTouchEvent就收不到事件了(包括down事件)}/*** 返回兩個點之間的距離,注意,如果只有一個觸摸點的話調用getX(1)就掛掉了,為減少冗余的判斷,我們可以放在try中*/private float spacing(MotionEvent event) {float x = event.getX(0) - event.getX(1);float y = event.getY(0) - event.getY(1);return FloatMath.sqrt(x * x + y * y);}}
縮放、移動圖片
public class MainActivity extends Activity {
private ImageView imageView;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);imageView = (ImageView) this.findViewById(R.id.imageView);imageView.setOnTouchListener(new TouchListener());}private final class TouchListener implements OnTouchListener {/** 記錄是拖拉照片模式還是放大縮小照片模式 */private int mode = 0;// 初始狀態/** 拖拉照片模式 */private static final int MODE_DRAG = 1;/** 放大縮小照片模式 */private static final int MODE_ZOOM = 2;/** 用於記錄開始時候的坐標位置 */private PointF startPoint = new PointF();/** 用於記錄拖拉圖片移動的坐標位置 */private Matrix matrix = new Matrix();/** 用於記錄圖片要進行拖拉時候的坐標位置 */private Matrix currentMatrix = new Matrix();/** 兩個手指的開始距離 */private float startDis;/** 兩個手指的中間點 */private PointF midPoint;@Overridepublic boolean onTouch(View v, MotionEvent event) {/** 通過與運算保留最后八位 MotionEvent.ACTION_MASK = 255 */switch (event.getAction() & MotionEvent.ACTION_MASK) {// 手指壓下屏幕case MotionEvent.ACTION_DOWN:mode = MODE_DRAG;// 記錄ImageView當前的移動位置currentMatrix.set(imageView.getImageMatrix());startPoint.set(event.getX(), event.getY());break;// 手指在屏幕上移動,該事件會被不斷觸發case MotionEvent.ACTION_MOVE:// 拖拉圖片if (mode == MODE_DRAG) {float dx = event.getX() - startPoint.x; // 得到x軸的移動距離float dy = event.getY() - startPoint.y; // 得到x軸的移動距離// 在沒有移動之前的位置上進行移動matrix.set(currentMatrix);matrix.postTranslate(dx, dy);}// 放大縮小圖片else if (mode == MODE_ZOOM) {float endDis = distance(event);// 結束距離if (endDis > 10f) { // 兩個手指並攏在一起的時候像素大於10float scale = endDis / startDis;// 得到縮放倍數matrix.set(currentMatrix);matrix.postScale(scale, scale, midPoint.x, midPoint.y);}}break;// 手指離開屏幕case MotionEvent.ACTION_UP:// 當觸點離開屏幕,但是屏幕上還有觸點(手指)case MotionEvent.ACTION_POINTER_UP:mode = 0;break;// 當屏幕上已經有觸點(手指),再有一個觸點壓下屏幕case MotionEvent.ACTION_POINTER_DOWN:mode = MODE_ZOOM;/** 計算兩個手指間的距離 */startDis = distance(event);/** 計算兩個手指間的中間點 */if (startDis > 10f) { // 兩個手指並攏在一起的時候像素大於10midPoint = mid(event);//記錄當前ImageView的縮放倍數currentMatrix.set(imageView.getImageMatrix());}break;}imageView.setImageMatrix(matrix);return true;}/** 計算兩個手指間的距離 */private float distance(MotionEvent event) {float dx = event.getX(1) - event.getX(0);float dy = event.getY(1) - event.getY(0);/** 使用勾股定理返回兩點之間的距離 */return FloatMath.sqrt(dx * dx + dy * dy);}/** 計算兩個手指間的中間點 */private PointF mid(MotionEvent event) {float midX = (event.getX(1) + event.getX(0)) / 2;float midY = (event.getY(1) + event.getY(0)) / 2;return new PointF(midX, midY);}}}