触摸事件【MotionEvent】简介
MotionEvent简介
当用户触摸屏幕时,将创建一个MontionEvent对象,MotionEvent包含了关于发生触摸的位置、时间信息,以及触摸事件的其他很多细节。Android 将所有的输入事件都放在了 MotionEvent 中,随着安卓的不断发展壮大,MotionEvent 也开始变得越来越复杂,下面是网上整理的 MotionEvent几次比较大的变动:
- Android 1.0 (API 1 ) 支持单点触控和轨迹球的事件。
- Android 1.6 (API 4 ) 支持手势。
- Android 2.0 (API 5 ) 支持多点触控。
- Android 3.1 (API 12) 支持触控笔,鼠标,键盘,操纵杆,游戏控制器等输入工具。
获取MontionEvent对象的方式主要有以下两种:
- 在View或Activity的onTouchEvent方法中
- 实现OnTouchListener接口后在onTouch方法中
MotionEvent官方文档翻译
MontionEvent描述了动作的动作代码和一些列的坐标值。动作代码表明了当触点按下或者弹起等引起的状态变化。坐标值描述了位置信息以及以他的运动属性。例如,当用户第一次触摸屏幕的时候,系统给窗体发出一个触摸事件,动作代码为ACTION_DOWN,并提供了一些列的坐标值,比如触摸的X、Y坐标,接触区域的压力、尺寸、方向等信息。一些设备能够在同一时间报告多条运动轨迹。多点触控屏幕为每个手指都发出一条运动轨迹。手指或者其他能够产生运动轨迹的物体都可以叫做触点。运动事件包含所有触点的信息,即使有些触点自从上次事件之后就没有再移动,这些触点必须是当前处于活动状态的。只有触点按下或者抬起的时候才会影响触点的数量,动作取消的时候除外。每个触点都有一个唯一的id,这个id是在触点第一次按下的时候(动作代码为ACTION_DOWN或者ACTION_POINTER_DOWN)由系统自动分配的。触点的id会一直保持有效,当触点抬起的时候(动作代码为ACTION_UP或者ACTION_POINTER_UP)或者动作取消(动作代码为ACTION_CANCEL)的时候会导致触点的id失效。MotionEvent类提供了许多可以查看触点的位置或者其他信息的方式,比如getX(int)、getY(int)、getAxisValue(int)、getPointerId(int)、getToolType(int)。这其中的大部分方法都将触点的索引值作为参数而不是触点的id。在事件中,每个触点的索引号的取值范围是从0到getPointerCount()-1。在一次运动中触点出现的顺序是不确定的。因此,触点的索引值会由于事件的变化而变化,但是只要触点处于活动状态,该触点的id就不会改变。用getPointerId(int)方法可以得到触点的id值,从而根据得到的id值在一连串的动作中来追踪其运动轨迹。然而在连续的运动事件中,应该用findPointerIndex(int)方法通过触点的id值得到触点的索引值。历史数据 批处理 Batching
由于Android设备对于触摸事件的反应非常灵敏,手指稍微移动一下就会产生一个移动事件,所以移动事件会产生的特别频繁。为了提高效率,系统会将近期的多个移动事件按照事件发生的顺序进行排序打包后放在同一个 MotionEvent 中,与之对应的产生了以下方法:
- getHistorySize() 获取历史事件集合大小
- getHistoricalX(int pos) 获取第pos个历史事件x坐标(pos < getHistorySize())
- getHistoricalX (int pin, int pos) 获取第pin个手指的第pos个历史事件x坐标(pin < getPointerCount(), pos < getHistorySize())
注意:
- pin全称是pointerIndex,表示第几个手指,此处为了节省空间使用了缩写。
- 历史数据只有 ACTION_MOVE 事件。
- 历史数据单点触控和多点触控均可以用。
官方文档翻译:为了提高效率,Android系统在处理ACTION_MOVE事件时可能会将连续的几个多触点移动事件打包到一个MotionEvent对象中,我们可以通过getX(int)和getY(int)来获得最近(当前)发生的一个触摸点事件的坐标,或使用getHistorical(int,int)和getHistorical(int,int)来获得时间稍早的触点事件的历史坐标,这些坐标之所以被成为"历史坐标",是因为这些坐标比当前坐标更早的出现了。要想按照时间顺序处理所有坐标,首先要处理通过getHistoricalXX相关函数获得的事件信息,然后再处理当前的事件信息。官方文档中例子: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));
}
}【常用API】
MotionEvent中的方法基本都是引用的native方法。最常用
- getAction() 获取事件类型,注意多点触控时获取到的是由pointer的index值和事件类型值组合而成的值
- getActionMasked() 与getAction()类似,多点触控时必须使用这个方法获取事件类型
- getActionIndex() 获取该事件是哪个指针(手指)产生的
- getPointerCount() 获取在屏幕上手指的个数
- getPointerId(int pointerIndex) 获取一个pointer的唯一标识符ID,在手指按下和抬起之间ID始终不变
- findPointerIndex(int pointerId) 通过PointerId获取到当前状态下PointIndex,之后便可以通过PointIndex获取其他内容
获取事件发生的位置坐标
- getX() 获取第1个触摸点触摸的中间区域相对当前 View 的 X 轴坐标
- getRawX() 和上面getX()不同的是,此方法获得的是相对屏幕的原始的位置的 X 轴坐标
- getX(int pointerIndex) 获取第pointerIndex个触控点触摸的中间区域相对当前 View 的 X 轴坐标
获取事件发生的时间
- getDownTime() 获取手指按下时的时间。返回值类型为 long,单位是毫秒
- getEventTime() 获取当前事件发生的时间。返回值类型为 long,单位是毫秒
获取压力和接触面积的大小
- getSize() 获取第1个手指与屏幕接触面积的大小
- getSize(int pin) 获取第pin个手指与屏幕接触面积的大小
- getPressure() 获取第一个手指的压力大小
- getPressure(int pin) 获取第pin个手指的压力大小
历史事件
- getHistorySize() 获取历史事件集合大小
- getHistoricalX(int pos) 获取第1个手指的第pos个历史事件x坐标(pos < getHistorySize())
- getHistoricalX (int pin, int pos) 获取第pin个手指的第pos个历史事件x坐标(pin < getPointerCount())
- getHistoricalEventTime(int pos) 获取历史事件发生的时间。返回值类型为 long,单位是毫秒
- getHistoricalSize(int pos) 获取历史数据中第1个手指在第pos次事件中的接触面积
- getHistoricalSize(int pin, int pos) 获取历史数据中第pin个手指在第pos次事件中的接触面积
- getHistoricalPressure(int pos) 获取历史数据中第1个手指在第pos次事件中的压力大小
- getHistoricalPressure(int pin, int pos) 获取历史数据中第pin个手指在第pos次事件中的压力大小
其他
- getToolType(int pin) 获取对应的输入设备类型
- getEventTime()-event.getDownTime()) 总共按下时花费时间
- getEdgeFlags() 当事件类型是ActionDown时可以通过此方法获得手指触控开始的边界,有如下几种值:EDGE_LEFT、EDGE_TOP、EDGE_RIGHT、EDGE_BOTTOM
注意:
- 获取接触面积大小和获取压力大小是需要硬件支持的。
- 非常不幸的是大部分设备所使用的电容屏不支持压力检测,但能够大致检测出接触面积。
- 大部分设备的 getPressure() 是使用接触面积来模拟的。
- 由于某些未知的原因(可能系统版本和硬件问题),某些设备不支持该方法。
结论:由于获取接触面积和获取压力大小受系统和硬件影响,使用的时候一定要进行数据检测,以防因为设备问题而导致程序出错。
单点触控的动作事件
ACTION_UP==1:表示用户抬起了手指(当屏幕上唯一的点被放开时触发)。
ACTION_MOVE==2:表示触摸点在移动(当有点在屏幕上移动时一直触发)。
ACTION_CANCEL==3:表示手势被取消了(事件被上层拦截了,由父View发送,不是用户自己触发的)。
ACTION_OUTSIDE==4:表示用户触碰超出了正常的UI边界(这是个奇葩)。
多点触控的动作事件
ACTION_POINTER_DOWN==5:当屏幕上已经有一个点被按住,此时再按下其他点时触发。
ACTION_POINTER_UP==6:在多个触摸点存在的情况下,其中一个触摸点消失了。此时,还有触摸点存在,也就是说用户还有手指触摸屏幕。相对而言,ACTION_UP可以说是最后一个触摸点消失时产生的。
- 先产生一个ACTION_DOWN事件,代表用户的第一个手指接触到了屏幕。
- 再产生一个ACTION_POINTER_DOWN事件,代表用户的第二个手指接触到了屏幕。
- 然后会产生很多的ACTION_MOVE事件,但是在这些MotionEvent对象中,都保存着两个触摸点滑动的信息。
- 再然后产生一个ACTION_POINTER_UP事件,代表用户的一个手指离开了屏幕。
- 此时如果剩下的那个手指还在滑动时,仍会产生很多ACTION_MOVE事件。
- 最后产生一个ACTION_UP事件,代表用户的最后一个手指离开了屏幕
多点触控的掩码常量
- ACTION_MASK==0x00ff:动作掩码常量。Bit位 mask屏蔽、掩码 of the parts of the action code that are the action itself.
- ACTION_POINTER_INDEX_MASK==0xff00:索引掩码常量。Bits in the action code that represent代表 a pointer index, used with {@link #ACTION_POINTER_DOWN} and {@link #ACTION_POINTER_UP}. Shifting移位 down by {@link #ACTION_POINTER_INDEX_SHIFT} provides the actual pointer index where the data for the pointer going up or down can be found; you can get its identifier with {@link #getPointerId(int)} and the actual data with {@link #getX(int)} etc. @see #getActionIndex
- ACTION_POINTER_INDEX_SHIFT==8:用于右移8位的常量,获取触摸点索引需要移动的位数。Bit shift for the action bits holding the pointer index as defined by {@link #ACTION_POINTER_INDEX_MASK}.
其他动作事件
- ACTION_HOVER_MOVE==7:指针在窗口或者View区域移动,但没有按下。
- ACTION_SCROLL==8:滚轮滚动,可以触发水平滚动(AXIS_HSCROLL)或者垂直滚动(AXIS_VSCROLL)
- ACTION_HOVER_ENTER==9:指针移入到窗口或者View区域,但没有按下。
- ACTION_HOVER_EXIT==10:指针移出到窗口或者View区域,但没有按下。
- ACTION_BUTTON_PRESS==11:按下按钮
- ACTION_BUTTON_RELEASE==12:释放按钮
输入设备类型判断
MotionEvent 负责集中处理所有类型设备的输入事件,主要包括以下几种:
- TOOL_TYPE_ERASER 橡皮擦
- TOOL_TYPE_FINGER 手指
- TOOL_TYPE_MOUSE 鼠标,在手机上使用概率也比较小
- TOOL_TYPE_STYLUS 手写笔、触控笔,处理流程基本相同
- TOOL_TYPE_UNKNOWN 未知类型
单点触控的事件处理
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:// 手指按下
break;
case MotionEvent.ACTION_MOVE:// 手指移动
break;
case MotionEvent.ACTION_UP:// 手指抬起
break;
case MotionEvent.ACTION_CANCEL:// 事件被拦截
break;
case MotionEvent.ACTION_OUTSIDE:// 超出区域
break;
}
return super.onTouchEvent(event);
}
关于事件类型与索引
可以通过getAction()或getActionMasked()获得事件的类型。getAction获得的int值是由pointer的index值和事件类型值组合而成的,而getActionWithMasked则只返回事件的类型值。因为android2.0之后一个MotionEvent对象中可以包含多个触摸点的事件,在MotionEvent对象只包含一个触摸点的事件时,上边两个函数的结果是相同的,但是如果包含多个触摸点,二者的结果就不同啦。1、getAction()和getActionMasked()有什么区别呢?如果Action的值是在0x00到0xff之间的话,getAction()返回的值和getActionMasked()的返回的值是一样的。
而当mAction的值大于0xff时,二者返回的值是不一样的。
2、什么时候mAction的值会大于0xff呢?这就是是当有多点触控时。我们知道Android是支持多点触控的,那么我们怎么知道这个MotionEvent是哪一个触控点触发的呢?这就需要MotionEvent带有触控点索引信息。Android的解决方案是在mAction的第二个8位中存储触控点索引信息。例如,如果mAction的值是0x0000,则表示是第一个触控点的ACTION_DOWN操作,如果mAction的值是0x0100呢,则表示是第二个触控点的ACTION_DOWN操作。总而言之,mAction时的低8位(0-7位)是动作类型信息,高8位(8-15位)是触控点的索引信息(即表示是哪一个触控点的事件)。event.getActionMasked() 和【event.getAction() & MotionEvent.ACTION_MASK】是等价的3、为什么不用两个字段来表示?因为动作类型只要0-255(2^8)就可以了,触控的个数也是,两者加在一起只需要16位即可。一个int类型占4个字节,即4*8=32位,所以完全可以用一个int类型的字段来存储动作类型和触控的个数这两个信息,这样即可以节约内存,又可以提高处理速度。同样的设计思想也体现在onMeasure中的MeasureSpec上。不过为了便利(可读性、独立性、解耦),通常我们都是以不同的字段来存储不同的信息的,虽然在内存上浪费了点。4、如何得索引值呢?原理:先将action跟0xff00相与清除前8位用于存储动作常量的信息,然后将action右移8位就可以得到索引值了。即先对action用ACTION_POINTER_INDEX_MASK进行掩码处理,即action & ACTION_POINTER_INDEX_MASK = action & 0xff00这个掩码也就是将action这个数的前8位清零.然后再将maskedIndex向右移8位就能够得到索引值了。android中用于右移8位的常量为:ACTION_POINTER_INDEX_SHIFT5、为什么要有索引信息?因为android中,当有触摸事件发生时(假设已经注册了事件监听器),会回调你注册的监听器中的onTouch(MotionEvent ev)方法传递了一个MotionEvent的对象过来。但是,通过上面的方法永远是只传递进来一个MotionEvent过来,如果只是单点触控那是没有问题,但是当你多个手指触控的时候你可能会需要知道每个手指的所对应的触控点的数据信息,所以MotionEvent中就必须要有索引信息。另外,通过API可以看到,MotionEvent还包含了移动操作中其它历史移动数据,方便处理触控的移动操作。
触摸事件【MotionEvent】简介的更多相关文章
- 触摸事件MotionEvent
触摸事件MotionEvent在用户交互中,占着非常重要的地位.首先,来看看MotionEvent中封装的一些常用的事件常量,它定义了触摸事件的不同类型. 1.单点触摸按下动作 public stat ...
- Android触摸事件MotionEvent详解
触摸事件MotionEvent在用户交互中,占着非常重要的地位.首先,来看看MotionEvent中封装的一些常用的事件常量,它定义了触摸事件的不同类型. 1.单点触摸按下动作 public stat ...
- Android 手势&触摸事件 MotionEvent
1.http://blog.csdn.net/omg_2012/article/details/7881443 这篇相当好啊 2.http://blog.csdn.net/android_tutor/ ...
- 触摸事件 Touch MotionEvent ACTION
MotionEvent简介 当用户触摸屏幕时,将创建一个MontionEvent对象,MotionEvent包含了关于发生触摸的位置.时间信息,以及触摸事件的其他细节. 获取MontionEvent对 ...
- 初识Android触摸事件传递机制
前言 今天总结的一个知识点是Andorid中View事件传递机制,也是核心知识点,相信很多开发者在面对这个问题时候会觉得困惑,另外,View的另外一个难题滑动冲突,比如在ScrollView中嵌套Li ...
- Android触摸事件的应用
前言 上一篇讲了Android触摸事件的传递机制,具体可以看这里 初识Android触摸事件传递机制.既然知道Android中触摸事件的传递分发,那么它能解决什么样的问题,在我们实际开发中如何应用,这 ...
- Android 手势&触摸事件
在刚开始学Android的时候,就觉得Google的文档不咋样,在研究手势时,更加的感觉Google的文档写得实在是太差了.很多常量,属性和方法,居然连个描述都没有. 没有描述也就罢了,但是OnGes ...
- iOS开发系列--触摸事件、手势识别、摇晃事件、耳机线控
-- iOS事件全面解析 概览 iPhone的成功很大一部分得益于它多点触摸的强大功能,乔布斯让人们认识到手机其实是可以不用按键和手写笔直接操作的,这不愧为一项伟大的设计.今天我们就针对iOS的触摸事 ...
- Android触摸事件流程剖析
Android中的触摸事件流程就是指MotionEvent如何传递,主要包括两个阶段: onInterceptTouchEvent触摸事件拦截方法传递,从外到里传递 onTouchEvent触摸事件处 ...
随机推荐
- 【WPF】奇怪的INotifyPropertyChanged的实现
MSDN是这样解释的: INotifyPropertyChanged 接口用于向客户端(通常是执行绑定的客户端)发出某一属性值已更改的通知. 例如,考虑一个带有名为 FirstName 属性的 Per ...
- 关于latex的画图
可以使用latex画一些简单的图 可以参考这个链接:http://www.latexstudio.net/archives/9400(PGFPlots绘图简易教程[转载])
- 洛谷P0248 [NOI2010] 超级钢琴 [RMQ,贪心]
题目传送门 超级钢琴 题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符 ...
- python笔记五:IO与文件
1.python IO: Python提供了两个内置函数从标准输入读入一行文本,默认的标准输入是键盘: 1)raw_input([prompt]) 函数从标准输入读取一个行,并返回一个字符串 2 ...
- PIPESTATUS(bash) + pipefail(ksh)
I have two processes foo and bar, connected with a pipe: $ foo | bar bar always exits 0; I'm interes ...
- spring完成自动装配
让spring完成自动装配 Autowiring 解决标签为javaBean注入时难以维护而实现的 下面是几种autowire type的说明: 1,byname:试图在容器中寻找和需要自动装配的属性 ...
- GeoIP的使用
GeoIP介绍: 什么是GepIP ? 所谓GeoIP,就是通过来访者的IP, 定位他的经纬度,国家/地区,省市,甚至街道等位置信息.这里面的技术不算难题,关键在于有个精准 的数据库.有了准确的数据源 ...
- 稀疏编码直方图----一种超越HOG的轮廓特征
该论文是一篇来自CMU 的CVPR2013文章,提出了一种基于稀疏编码的轮廓特征,简称HSC(Histogram of Sparse Code),并在目标检测中全面超越了HOG(Histogram o ...
- C++继承引入的隐藏与重写
在区分隐藏和重写之前,先来理一理关于继承的东西... [继承] 继承是面向对象复用的重要手段,是类型之间的关系建模.通过继承一个类,共享公有的东西,实现各自本质不同的东西.简单的说,继承就是指一个对象 ...
- python模块整理29-redis模块
date:20140530auth:jinhttp://github.com/andymccurdy/redis-pyhttps://github.com/andymccurdy/redis-py/b ...