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;//記錄手指全部離開時的縮放比例,下次縮放時是在次基礎上進行的
@Override
protected 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);
}
@Override
public 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;
@Override
public 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;
@Override
public 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) { // 兩個手指並攏在一起的時候像素大於10
float 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) { // 兩個手指並攏在一起的時候像素大於10
midPoint = 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);
}
}
}
