Android事件处理之MotionEvent
2014-01-06 15:41
190 查看
我们不讨论按键,滚动球,鼠标事件,我们这里事件特指触摸事件。在Android中,谈到事件,有两个需要考虑的地方,一个就是事件本身,这是一个什么事件,这个事件有哪些信息等,这就是我们本节讨论的内容,事件的所有属性都存储在MotionEvent这一个类的对象中,另一个需要考虑的是事件的处理,怎么对不同的事件进行不同的处理,在什么地方处理事件等,这部分内容参照之前的博客:
进入本文主题,MotionEvent.java是一个属性类,它与本地函数想通,我们每一次的触摸事件都会对应生成一个MotionEvent对象(这个对象由Activity.dispatchTouchEvent(MotionEvent)分发),对象中保存着本次事件的位置,事件类型,等等信息。而MotionEvent也提供了相应的各种函数,供我们获取这些信息,而事件处理正是根据获取到的信息进行不同的分发处理的。
MotionEvent的作用就是一种抽象,它把我们在触摸屏上的触摸事件抽象成了可以表示的符号,属性等,这是面向对象的好处。例如手指按下这一动作,对应成了一个MotionEvent对象,这个对象里储存着一个int=0(ACTION_DOWN)的值,以及手指在屏幕上的位置等等信息值。
因此对于MotionEvent类,我们需要理解的是它的各种属性和函数对应的实际效果是什么,也就是需要深度理解MotionEventAPI的意思,源码中有非常详细的解释,当然也有些函数是本地函数,通过官方API也可以详细知晓各个函数的作用。
MotionEvent中属性和函数众多,这里选取常用的触摸属性和函数作出解释记录:
ACTION_DOWN(0x00000000):表示用户开始触摸.
ACTION_MOVE(0x00000002):表示用户在移动(手指或者其他)
ACTION_UP(0x00000001):表示用户抬起了手指
ACTION_CANCEL(0x00000003):表示手势被取消了,一些关于这个事件类型的讨论见:http://stackoverflow.com/questions/11960861/what-causes-a-motionevent-action-cancel-in-android
ACTION_POINTER_DOWN(0x00000005):有一个非主要的手指按下了.
ACTION_POINTER_UP(0x00000006):一个非主要的手指抬起来了
上面这些就是我们常用的事件在MotionEvent中抽象出来的属性值。
这里我们提一下现在已经废止不用的事件,比如,ACTION_POINTER_2_UP(0x00000106),还有一个常数值ACTION_MASK(0x000000ff),以及它们所涉及的getAction()和getActionMasked()的区别。我们先来看看这两个函数的源码
下面转载的这部分内容对这一内容作了充分的诠释:
/article/3475214.html
他们有什么区别呢?如果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),为什么不用两个字段来表示。
如intmAction,intmPointer,
mAction表示动作类型,mPointer表示第几个触控点。
因为,动作类型只要0-255就可以了,动作类型,mPointer也是。
只要一个字段(32位),否则需要两个字段(32*2=64位),即可以节约内存。又可以方便提高处理速度。
不过通常我们都是以不同的字段来存储不同的信息。但是在计算机内部他们还是变成了0,1。
计算机始终还是以位来存储信息的。如果我们多我熟悉以位为基本单位来理解信息的存储。对于理解android中的很多变量是很有帮助的。因为他其中的很多东西使用的这样的节约内在的技巧。
如onMeasure中的MeasureSpec。
先看关于这两个方法注释:
我简单的翻译如下:
在上面的两个方法中注释出现差异的地方是对于ACTION_POINTER_DOWN的描述:
通过getAction()返回的ACTION_POINTER_DOWN的是与转换触控点索引的合成值.
而getActionMasked()则就是一个ACTION_POINTER_DOWN的值:
这么来看我们知道一个action的代码值还包含了action是那个触控点的索引值:
现在我们对比来看看ACTION_MASK和ACTION_POINTER_INDEX_MASK
还没有看出来什么吗?
您把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位的常量.
再年得到索引值方法源代码,如下:
为什么要有索引信息?
因为,这样说吧,android中,当有触摸事件发生时(假设已经注册了事件监听器),调用你注册监听器中的方法onTouch(,MotionEventev);传递了一个MotionEvent的对象过来.
但是,想想,上面只传递进来一个MotionEvent过来,如果只是单点触控那是没有问题.
问题就是当你多个手指触控的时候也是只传递这一个MotionEvent进来,
这个时候,你当然想知道每个手指的所对应的触控点数据信息啦.
所以MotionEvent中有就要索引信息了.
事件是你可以很容易通过API看到,MotionEvent还包含了移动操作中其它历史移动数据.
方便处理触控的移动操作.
androidsdk对于这个类的描述中就有这么一句:
Forefficiency,motioneventswithACTION_MOVEmaybatchtogethermultiplemovementsampleswithinasingleobject.
我翻译下:"出于效率的考虑,事件代码为ACTION_MOVE的Motion,会在一个MotionEvent对象中包含多个移动数据采样."
上面这部分内容主要关于多点触控,也是getAction()和getActionMasked()主要的区别所在,如果只考虑单点,那么我们仅仅使用getAction()就足矣。
下面介绍MotionEvent提供的常用函数
getAction():事件类型
getDownTime():Down事件发生时的时间
getEventTime():事件发生时的时间
getRawX():事件触碰点相对于手机屏幕左上角(全屏)的坐标
getRawY():同上
getSize():单点触碰手指触摸屏幕的区域大小
getX():事件相对于本视图原点的坐标
getY():同上
staticMotionEventobtain(longdownTime,longeventTime,intaction,floatx,floaty,intmetaState):静态方法,模拟生成一个事件。
了解了上述内容,我们就可以通过Activity的事件分发机制,对各个触碰事件进行处理,甚至我们可以组合各个基本事件创造复杂事件。
手势检测GestureDetector的实现原理
进入本文主题,MotionEvent.java是一个属性类,它与本地函数想通,我们每一次的触摸事件都会对应生成一个MotionEvent对象(这个对象由Activity.dispatchTouchEvent(MotionEvent)分发),对象中保存着本次事件的位置,事件类型,等等信息。而MotionEvent也提供了相应的各种函数,供我们获取这些信息,而事件处理正是根据获取到的信息进行不同的分发处理的。MotionEvent的作用就是一种抽象,它把我们在触摸屏上的触摸事件抽象成了可以表示的符号,属性等,这是面向对象的好处。例如手指按下这一动作,对应成了一个MotionEvent对象,这个对象里储存着一个int=0(ACTION_DOWN)的值,以及手指在屏幕上的位置等等信息值。
因此对于MotionEvent类,我们需要理解的是它的各种属性和函数对应的实际效果是什么,也就是需要深度理解MotionEventAPI的意思,源码中有非常详细的解释,当然也有些函数是本地函数,通过官方API也可以详细知晓各个函数的作用。
MotionEvent中属性和函数众多,这里选取常用的触摸属性和函数作出解释记录:
ACTION_DOWN(0x00000000):表示用户开始触摸.
ACTION_MOVE(0x00000002):表示用户在移动(手指或者其他)
ACTION_UP(0x00000001):表示用户抬起了手指
ACTION_CANCEL(0x00000003):表示手势被取消了,一些关于这个事件类型的讨论见:http://stackoverflow.com/questions/11960861/what-causes-a-motionevent-action-cancel-in-android
ACTION_POINTER_DOWN(0x00000005):有一个非主要的手指按下了.
ACTION_POINTER_UP(0x00000006):一个非主要的手指抬起来了
上面这些就是我们常用的事件在MotionEvent中抽象出来的属性值。
这里我们提一下现在已经废止不用的事件,比如,ACTION_POINTER_2_UP(0x00000106),还有一个常数值ACTION_MASK(0x000000ff),以及它们所涉及的getAction()和getActionMasked()的区别。我们先来看看这两个函数的源码
publicfinalintgetAction(){ returnnativeGetAction(mNativePtr); } publicfinalintgetActionMasked(){ returnnativeGetAction(mNativePtr)&ACTION_MASK; }
下面转载的这部分内容对这一内容作了充分的诠释:
他们有什么区别呢?如果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),为什么不用两个字段来表示。
如intmAction,intmPointer,
mAction表示动作类型,mPointer表示第几个触控点。
因为,动作类型只要0-255就可以了,动作类型,mPointer也是。
只要一个字段(32位),否则需要两个字段(32*2=64位),即可以节约内存。又可以方便提高处理速度。
不过通常我们都是以不同的字段来存储不同的信息。但是在计算机内部他们还是变成了0,1。
计算机始终还是以位来存储信息的。如果我们多我熟悉以位为基本单位来理解信息的存储。对于理解android中的很多变量是很有帮助的。因为他其中的很多东西使用的这样的节约内在的技巧。
如onMeasure中的MeasureSpec。
先看关于这两个方法注释:
我简单的翻译如下:
01 | /** |
02 | *action码的位掩码部分就是action本身 |
03 | */ |
04 | public static final int
0xff ; |
05 |
06 | /** |
07 | 返回action的类型,考虑使用getActionMasked()和getActionIndex()来获得单独的经过掩码的action和触控点的索引. |
08 | @returnaction例如ACTION_DOWN或者ACTION_POINTER_DOWN与转换的触控点索引的合成值 |
09 | */ |
10 | public final int getAction(){ |
11 | return mAction; |
12 | } |
13 |
14 | /** |
15 | 返回经过掩码的action,没有触控点索引信息. |
16 | 通过getActionIndex()来得到触控操作点的索引. |
17 | @returnaction,例如ACTION_DOWN,ACTION_POINTER_DOWN |
18 |
19 |
20 | */ |
21 | public final int getActionMasked(){ |
22 | return mAction&ACTION_MASK; |
23 | } |
通过getAction()返回的ACTION_POINTER_DOWN的是与转换触控点索引的合成值.
而getActionMasked()则就是一个ACTION_POINTER_DOWN的值:
这么来看我们知道一个action的代码值还包含了action是那个触控点的索引值:
现在我们对比来看看ACTION_MASK和ACTION_POINTER_INDEX_MASK
1 | public static final int ACTION_MASK= 0xff ; |
2 | 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位的常量.
1 | /** |
2 | *Bitshiftfortheactionbitsholdingthepointerindexas |
3 | *definedby{<ahref="http://my.oschina.net/link1212"target="_blank"rel="nofollow">@link</a>#ACTION_POINTER_INDEX_MASK}. |
4 | */ |
5 | public static final int
8 ; |
1 | public final int getActionIndex(){ |
2 | return (mAction&ACTION_POINTER_INDEX_MASK)>>ACTION_POINTER_INDEX_SHIFT; |
3 | } |
因为,这样说吧,android中,当有触摸事件发生时(假设已经注册了事件监听器),调用你注册监听器中的方法onTouch(,MotionEventev);传递了一个MotionEvent的对象过来.
但是,想想,上面只传递进来一个MotionEvent过来,如果只是单点触控那是没有问题.
问题就是当你多个手指触控的时候也是只传递这一个MotionEvent进来,
这个时候,你当然想知道每个手指的所对应的触控点数据信息啦.
所以MotionEvent中有就要索引信息了.
事件是你可以很容易通过API看到,MotionEvent还包含了移动操作中其它历史移动数据.
方便处理触控的移动操作.
androidsdk对于这个类的描述中就有这么一句:
Forefficiency,motioneventswithACTION_MOVEmaybatchtogethermultiplemovementsampleswithinasingleobject.
我翻译下:"出于效率的考虑,事件代码为ACTION_MOVE的Motion,会在一个MotionEvent对象中包含多个移动数据采样."
上面这部分内容主要关于多点触控,也是getAction()和getActionMasked()主要的区别所在,如果只考虑单点,那么我们仅仅使用getAction()就足矣。
下面介绍MotionEvent提供的常用函数
getAction():事件类型
getDownTime():Down事件发生时的时间
getEventTime():事件发生时的时间
getRawX():事件触碰点相对于手机屏幕左上角(全屏)的坐标
getRawY():同上
getSize():单点触碰手指触摸屏幕的区域大小
getX():事件相对于本视图原点的坐标
getY():同上
staticMotionEventobtain(longdownTime,longeventTime,intaction,floatx,floaty,intmetaState):静态方法,模拟生成一个事件。
了解了上述内容,我们就可以通过Activity的事件分发机制,对各个触碰事件进行处理,甚至我们可以组合各个基本事件创造复杂事件。
相关文章推荐
- Android 笔记 motionEvent 处理触摸事件
- Android的MotionEvent和事件处理
- android源码分析——事件输入流程MotionEvent事件处理流程
- android 4.0.1 webkit Event 事件处理过程分析
- android 事件处理机制——requestDisallowInterceptTouchEvent
- Android捕获touchEvent事件处理
- android 事件处理机制之requestDisallowInterceptTouchEvent
- Android疯狂讲义中,基于回调的事件处理,报错Binary XML file line #8: Error inflating class org.crazyit.event.MyButton
- android 事件处理--onInterceptTouchEvent和onTouchEvent调用时序(二)
- android事件拦截处理机制---详解 onInterceptTouchEven onTouchEvent
- Android MotionEvent事件分发机制源码剖析
- android事件处理总结--dispatchTouchEvent
- Android之触摸事件MotionEvent
- Android事件处理之View$dispatchTouchEvent(ev)
- 详细剖析 android onInterceptTouchEvent(MotionEvent event) 和 onTouchEvent(MotionEvent event) 的事件传递机制
- 关于Android自定义View中的onTouchEvent(MotionEvent event)事件监听
- android事件处理总结--dispatchTouchEvent
- android onTouchEvent 左右手势滑动事件处理
- Android触摸事件处理机制之requestDisallowInterceptTouchEvent
- Android开发:在onTouchEvent中处理任意时间的长按事件