这是我我的的见解,要学好android触控,了解MotionEvent是必要,对所用的MotionEvent经常使用的API要比较深刻的了解. java
下面是我我的的学习过程记录: android
android.view.MotionEvent git
MotionEvent源代码能够在ocs看到,固然你也能够在SDK中下载源代码,或者其余地方,如: github
通常咱们是在View的onTouchEvent方法中处理MotionEvent对象的. 学习
public boolean onTouchEvent(MotionEvent event) spa
在这里咱们须要从一个MotionEvent对象中得到哪些信息呢? .net
能够经过getAction(),在android2.2以后加入多点触控支持以后使用getActionMasked()方法. 翻译
这两个方法的区别见后文. code
主要的事件类型有:
ACTION_DOWN: 表示用户开始触摸.
ACTION_MOVE: 表示用户在移动(手指或者其余)
ACTION_UP:表示用户抬起了手指
ACTION_CANCEL:表示手势被取消了,一些关于这个事件类型的讨论见:http://stackoverflow.com/questions/11960861/what-causes-a-motionevent-action-cancel-in-android
还有一个不常见的:
ACTION_OUTSIDE: 表示用户触碰超出了正常的UI边界.
可是对于多点触控的支持,Android加入了如下一些事件类型.来处理,如另外有手指按下了,
有的手指抬起来了.等等:
ACTION_POINTER_DOWN:有一个非主要的手指按下了.
ACTION_POINTER_UP:一个非主要的手指抬起来了
getX() 得到事件发生时,触摸的中间区域在屏幕的X轴.
getY() 得到事件发生时,触摸的中间区域在屏幕的X轴.
在多点触控中还能够经过:
getX(int pointerIndex) ,来得到对应手指事件的发生位置. 得到Y轴用getY(int pointerIndex)
getEdgeFlags():当事件类型是ActionDown时能够经过此方法得到,手指触控开始的边界. 若是是的话,有以下几种值:EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM
(1)首先是MotionEvent 中getAction()与getActionMasked()的区别:
首先看代码:
/** * Bit mask of the parts of the action code that are the action itself. */ public static final int ACTION_MASK = 0xff; /** * Return the kind of action being performed. * Consider using {@link #getActionMasked} and {@link #getActionIndex} to retrieve * the separate masked action and pointer index. * @return The action, such as {@link #ACTION_DOWN} or * the combination of {@link #ACTION_POINTER_DOWN} with a shifted pointer index. */ public final int getAction() { return mAction; } /** * Return the masked action being performed, without pointer index information. * Use {@link #getActionIndex} to return the index associated with pointer actions. * @return The action, such as {@link #ACTION_DOWN} or {@link #ACTION_POINTER_DOWN}. */ public final int getActionMasked() { return mAction & ACTION_MASK; }
上面的代码是基于android2.2的,注释是android4.X中最新的.
他们有什么区别呢?若是mAction的值是在0x00到0xff之间的话。getAction()返回的值,和
getActionMasked()的返回的值是同样的。
(Q1)那何时返回的值是同样的呢?即当mAction值大于0xff时,那何时会大于0xff呢?
这就是是当有多点触控时。当有多点触控时。
mAction的低8位即0x00到0xff用来表示动做的类型信息。
例如:MotionEvent#ACTION_DOWN的值是 0,即0x00。
MotionEvent#ACTION_UP的值是 1,即0x01。
等等。
可是,咱们知道Android是支持多点触控的,那么怎么知道这个一个MotionEvent是哪个
触控点触发的呢?那么就还须要MotionEvent带有触控点索引信息。
Android的解决方案时在;mAction的第二个8位中存储。
例如,若是mAction的值是0x0000,则表示是第一个触控点的ACTION_DOWN操做。
若是mAction的值是0x0100呢,则表示是第二个触控点的ACTION_DOWN操做。
第三个的ACTION_DOWN呢?相信你能够推出来是0x0200。
总而言之,mAction时的低8位(也就是0-7位)是动做类型信息。
mAction的8-15位呢,是触控点的索引信息。(即表示是哪个触控点的事件)。
(Q2),为何不用两个字段来表示。
如 int mAction,int mPointer,
mAction表示动做类型,mPointer表示第几个触控点。
由于,动做类型只要0-255就能够了,动做类型,mPointer也是。
只要一个字段(32位),不然须要两个字段(32*2=64位),便可以节约内存。又能够方便提升处理速度。
不过一般咱们都是以不一样的字段来存储不一样的信息。可是在计算机内部他们仍是变成了0,1。
计算机始终仍是以位来存储信息的。若是咱们多我熟悉以位为基本单位来理解信息的存储。对于理解android中的不少变量是颇有帮助的。由于他其中的不少东西使用的这样的节约内在的技巧。
如onMeasure中的MeasureSpec。
=================== the above is update at 2013-01-24=====
先看关于这两个方法注释:
我简单的翻译以下:
/** * action码的位掩码部分就是action自己 */ public static final int ACTION_MASK = 0xff; /** 返回action的类型,考虑使用getActionMasked()和getActionIndex()来得到单独的通过掩码的action和触控点的索引. @return action例如ACTION_DOWN或者ACTION_POINTER_DOWN与转换的触控点索引的合成值 */ public final int getAction() { return mAction; } /** 返回通过掩码的action,没有触控点索引信息. 经过getActionIndex()来获得触控操做点的索引. @return action,例如ACTION_DOWN,ACTION_POINTER_DOWN */ public final int getActionMasked() { return mAction & ACTION_MASK; }
在上面的两个方法中注释出现差别的地方是对于ACTION_POINTER_DOWN的描述:
经过getAction()返回的ACTION_POINTER_DOWN的是与转换触控点索引的合成值.
而getActionMasked()则就是一个ACTION_POINTER_DOWN的值:
这么来看咱们知道一个action的代码值还包含了action是那个触控点的索引值:
如今咱们对比来看看ACTION_MASK和ACTION_POINTER_INDEX_MASK
public static final int ACTION_MASK = 0xff; public static final int ACTION_POINTER_INDEX_MASK = 0xff00;尚未看出来什么吗?
您把ACTION_MASK当作是0x00ff
就知道了吧.
也就是说,一个MotionEvent中的action代码,
前8位是实实在在包含表示哪个动做常量.
后八位呢就是包含了触控点的索引信息.
由于ACTION_MASK = 0x00ff因此,通过ACTION_MASK掩码事后的action码就没有索引信息了.
如何得索引值呢?
原理:
先将action跟0xff00相与清除前8位用于存储动做常量的信息,
而后将action右移8位就能够获得索引值了.
咱们就能够本身想办法获得索引信息了.
即先对action用ACTION_POINTER_INDEX_MASK进行掩码处理,
即 maskedIndex = action&ACTION_POINTER_INDEX_MASK = action&0xff00
这各掩码也就是将action这个数的前8位清零.
而后再将maskedIndex向右移8位就可以获得索引值了.
再看看android真实是怎么作的吧,
用于右移8位的常量.
/** * Bit shift for the action bits holding the pointer index as * defined by {@link #ACTION_POINTER_INDEX_MASK}. */ public static final int ACTION_POINTER_INDEX_SHIFT = 8;
再年获得索引值方法源代码,以下:
public final int getActionIndex() { return (mAction & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT; }
为何要有索引信息?
由于,这样说吧,android中,当有触摸事件发生时(假设已经注册了事件监听器),调用你注册监听器中的方法onTouch(,MotionEvent ev);传递了一个MotionEvent的对象过来.
可是,想一想,上面只传递进来一个MotionEvent过来,若是只是单点触控那是没有问题.
问题就是当你多个手指触控的时候也是只传递这一个MotionEvent进来,
这个时候,你固然想知道每一个手指的所对应的触控点数据信息啦.
因此MotionEvent中有就要索引信息了.
事件是你能够很容易经过API看到,MotionEvent还包含了移动操做中其它历史移动数据.
方便处理触控的移动操做.
android sdk对于这个类的描述中就有这么一句:
For efficiency, motion events with ACTION_MOVE may batch together multiple movement samples within a single object.
我翻译下:"出于效率的考虑,事件代码为ACTION_MOVE的Motion,会在一个MotionEvent对象中包含多个移动数据采样."
如今咱们对于MotionEvent有了初步的了解了.
PS:
我发现android4中MotionEvent中的代码大多变成了原生代码了:
例如如getX(int)在2.2中是这样的:
public final float getX(int pointerIndex) { return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_X]; }但到了4.x是这样的了:
public final float getX(int pointerIndex) { return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT); }是否是进步了呢?哈哈!