Android用一个32位的整型值表示一次TouchEvent事件,那就是MotionEvent,包括坐标信息、点击等事件,下面总结下这个类。
获取事件坐标
getX()和getY()
由这两个函数获得的x,y值是相对的坐标值,相对于消费这个事件的视图的左上点的坐标。getRawX()和getRawY()
由这两个函数获得的x,y值是绝对坐标,是相对于屏。
引用两张网上的图来说明:
获取事件类型
获取事件类型涉及到三个方法:getAction(),getActionMasked(),getActionIndex()。
1. getAction:触摸动作的原始32位信息,包括事件的动作,触控点信息
2. getActionMasked:触摸的动作,按下,抬起,滑动,多点按下,多点抬起
3. getActionIndex:触控点信息
以下是SDK中三个方法的定义:
public final int getAction() {
return nativeGetAction(mNativePtr);
}
public final int getActionMasked() {
return nativeGetAction(mNativePtr) & ACTION_MASK;
}
public final int getActionIndex() {
return (nativeGetAction(mNativePtr) & ACTION_POINTER_INDEX_MASK)
>> ACTION_POINTER_INDEX_SHIFT;
}
从上面方法可以看出getActionMasked()是getAction()的低8位,getActionIndex()是getAction()的高8位。
- getAction()的高8位,即getActionIndex()是用来记录多点触控时候的触控点信息的
单点触控的时候,getAction()等于getActionMasked(),getActionIndex()等于0
如果getAction()的值是0x0000,则表示是第一个触控点的ACTION_DOWN操作。
如果getAction()的值是0x0100呢,则表示是第二个触控点的ACTION_DOWN操作。第三个的ACTION_DOWN呢?相信你可以推出来是0x0200。
获取触控时间
- getDownTime()
指触摸事件开始的时间,即按下的时间,这时候会收到ACTION_DOWN的事件。 - getEventTime()
指触摸事件结束的时间,这时候会收到ACTION_UP或者ACTION_CANCEL事件
getEventTime()与getDownTime()的差就是本次事件总共花费的时间。
多点触控
为了可以表示多个触摸点的动作,MotionEvent中引入了Pointer的概念,一个pointer就代表一个触摸点,每个pointer都有自己的事件类型,也有自己的横轴坐标值。一个MotionEvent对象中可能会存储多个pointer的相关信息,每个pointer都会有一个自己的id和index。pointer的id在整个事件流中是不会发生变化的,但是index会发生变化。
MotionEvent类中的很多方法都是可以传入一个int值作为参数的,其实传入的就是pointer的index值。比如getX(pointerIndex)和getY(pointerIndex),此时,它们返回的就是index所代表的触摸点相关事件坐标值。
由于pointer的index值在不同的MotionEvent对象中会发生变化,但是id值却不会变化。所以,当我们要记录一个触摸点的事件流时,就只需要保存其id,然后使用findointerIndex(int)来获得其index值,然后再获得其他信息。
private final static int INVALID_ID = -1;
private int mActivePointerId = INVALID_ID;
private int mSecondaryPointerId = INVALID_ID;
private float mPrimaryLastX = -1;
private float mPrimaryLastY = -1;
private float mSecondaryLastX = -1;
private float mSecondaryLastY = -1;
public boolean onTouchEvent(MotionEvent event)
{
int action = MotionEventCompat.getActionMasked(event);
switch (action)
{
case MotionEvent.ACTION_DOWN:
int index = event.getActionIndex();
mActivePointerId = event.getPointerId(index);
mPrimaryLastX = MotionEventCompat.getX(event,index);
mPrimaryLastY = MotionEventCompat.getY(event,index);
break;
case MotionEvent.ACTION_POINTER_DOWN:
index = event.getActionIndex();
mSecondaryPointerId = event.getPointerId(index);
mSecondaryLastX = event.getX(index);
mSecondaryLastY = event.getY(index);
break;
case MotionEvent.ACTION_MOVE:
index = event.findPointerIndex(mActivePointerId);
int secondaryIndex = MotionEventCompat.findPointerIndex(event,mSecondaryPointerId);
final float x = MotionEventCompat.getX(event,index);
final float y = MotionEventCompat.getY(event,index);
final float secondX = MotionEventCompat.getX(event,secondaryIndex);
final float secondY = MotionEventCompat.getY(event,secondaryIndex);
break;
case MotionEvent.ACTION_POINTER_UP: break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mActivePointerId = INVALID_ID;
mPrimaryLastX =-1;
mPrimaryLastY = -1;
break;
}
return true;
}
除了pointer的概念,MotionEvent还引入了两个事件类型:
• ACTION_POINTER_DOWN:代表用户又使用一个手指触摸到屏幕上,也就是说,在已经有一个触摸点的情况下,有新出现了一个触摸点。
• ACTION_POINTER_UP:代表用户的一个手指离开了触摸屏,但是还有其他手指还在触摸屏上。也就是说,在多个触摸点存在的情况下,其中一个触摸点消失了。它与ACTION_UP的区别就是,它是在多个触摸点中的一个触摸点消失时(此时,还有触摸点存在,也就是说用户还有手指触摸屏幕)产生,而ACTION_UP可以说是最后一个触摸点消失时产生。
那么,用户先两个手指先后接触屏幕,同时滑动,然后在先后离开这一套动作所产生的事件流是什么样的呢?
它所产生的事件流如下:
•先产生一个ACTION_DOWN事件,代表用户的第一个手指接触到了屏幕。
•再产生一个ACTION_POINTER_DOWN事件,代表用户的第二个手指接触到了屏幕。
•很多的ACTION_MOVE事件,但是在这些MotionEvent对象中,都保存着两个触摸点滑动的信息,相关的代码我们会在文章的最后进行演示。
•一个ACTION_POINTER理理
UP事件,代表用户的一个手指离开了屏幕。
•如果用户剩下的手指还在滑动时,就会产生很多ACTION_MOVE事件。
•一个ACTION_UP事件,代表用户的最后一个手指离开了屏幕
批处理
为了效率,Android系统在处理ACTION_MOVE事件时会将连续的几个多触点移动事件打包到一个MotionEvent对象中。我们可以通过getX(int)和getY(int)来获得最近发生的一个触摸点事件的坐标,然后使用getHistorical(int,int)和getHistorical(int,int)来获得时间稍早的触事件的坐标,二者是发生时间先后的关系。所以,我们应该先处理通过getHistoricalXX相关函数获得的事件信息,然后在处理当前的事件信息。
下边就是Android Guide中相关的例子:
void printSamples(MotionEvent ev)
{
final int historySize = ev.getHistorySize();
final int pointerCount = ev.getPointerCount();
for (int h = 0; h < historySize; h++)
{
System.out.printf("At time %d:", ev.getHistoricalEventTime(h));
for (int p = 0; p < pointerCount; p++)
{
System.out.printf(" pointer %d: (%f,%f)", ev.getPointerId(p), ev.getHistoricalX(p, h), ev.getHistoricalY(p, h));
}
}
System.out.printf("At time %d:", ev.getEventTime());
for (int p = 0; p < pointerCount; p++)
{
System.out.printf(" pointer %d: (%f,%f)", ev.getPointerId(p), ev.getX(p), ev.getY(p));
}
}