概述
Android 将所有的输入事件都放在了 MotionEvent 中,随着安卓的不断发展壮大,MotionEvent 也开始变得越来越复杂。主要学一下单点触控、多点触控、鼠标事件 以及 getAction() 和 getActionMasked()
单点触控
涉及事件:
方法:
1 | event.getX(); //触摸点相对于其所在组件坐标系的坐标 |
针对单点触控的事件处理一般是这样写的:
1 |
|
理解 ACTION_CANCEL
ACTION_CANCEL
的触发条件是事件被上层拦截,我们知道当事件被上层 View 拦截的时候,ChildView 是收不到任何事件的,ChildView 收不到任何事件,自然也不会收到 ACTION_CANCEL
了,所以说这个 ACTION_CANCEL
的正确触发条件并不是这样,那么是什么呢?
事实上,只有上层 View 回收事件处理权的时候,ChildView 才会收到一个 ACTION_CANCEL
事件。
例如:上层 View 是一个 RecyclerView,它收到了一个 ACTION_DOWN
事件,由于这个可能是点击事件,所以它先传递给对应 ItemView,询问 ItemView 是否需要这个事件,然而接下来又传递过来了一个 ACTION_MOVE
事件,且移动的方向和 RecyclerView 的可滑动方向一致,所以 RecyclerView 判断这个事件是滚动事件,于是要收回事件处理权,这时候对应的 ItemView 会收到一个 ACTION_CANCEL
,并且不会再收到后续事件。
多点触控
比较复杂,后面再学。
getAction() 与 getActionMasked()
当多个手指在屏幕上按下的时候,会产生大量的事件,如何在获取事件类型的同时区分这些事件就是一个大问题了。一般来说我们可以通过为事件添加一个int类型的index属性来区分,但为了添加一个通常数值不会超过10的index属性就浪费一个int大小的空间简直是不能忍受的,于是工程师们将这个index属性和事件类型直接合并了。
int类型共32位(0x00000000),他们用最低8位(0x000000ff)表示事件类型,再往前的8位(0x0000ff00)表示事件编号,以手指按下为例讲解数值是如何合成的:
ACTION_DOWN 的默认数值为 (0x00000000)
ACTION_POINTER_DOWN 的默认数值为 (0x00000005)
注意:
上面表格中用粗体标示出的数值,可以看到随着按下手指数量的增加,这个数值也是一直变化的,进而导致我们使用 getAction()
获取到的数值无法与标准的事件类型进行对比,为了解决这个问题,他们创建了一个 getActionMasked()
方法,这个方法可以清除index数值,让其变成一个标准的事件类型。
1、多点触控时必须使用 getActionMasked()
来获取事件类型。
2、单点触控时由于事件数值不变,使用 getAction()
和 getActionMasked()
两个方法都可以。
3、使用 getActionIndex() 可以获取到这个index数值。不过请注意,getActionIndex() 只在 down 和 up 时有效,move 时是无效的。
PointId
虽然前面刚刚说了一个 actionIndex,可以使用 getActionIndex() 获得,但通过 actionIndex 字面意思知道,这个只表示事件的序号,而且根据其说明文档解释,这个 ActionIndex 只有在手指按下(down)和抬起(up)时是有用的,在移动(move)时是没有用的.
PointId 在手指按下时产生,手指抬起或者事件被取消后消失,是一个事件流程中唯一不变的标识,可以在手指按下时 通过 getPointerId(int pointerIndex)
获得。 (参数中的 pointerIndex 就是 actionIndex)。
因此追踪事件流就采用 PointId。
历史数据(批处理)
由于我们的设备非常灵敏,手指稍微移动一下就会产生一个移动事件,所以移动事件会产生的特别频繁,为了提高效率,系统会将近期的多个移动事件(move)按照事件发生的顺序进行排序打包放在同一个 MotionEvent 中,与之对应的产生了以下方法:
注意:
- pin 全称是 pointerIndex,表示第几个手指,此处为了节省空间使用了缩写。
- 历史数据只有 ACTION_MOVE 事件。
- 历史数据单点触控和多点触控均可以用。
看一下官方给的简单示例:
1 | void printSamples(MotionEvent ev) { |
获取事件发生的时间
- pos 表示历史数据中的第几个数据。( pos < getHistorySize() )
- 返回值类型为 long,单位是毫秒。
获取压力(接触面积大小)
MotionEvent支持获取某些输入设备(手指或触控笔)的与屏幕的接触面积和压力大小,主要有以下方法:
描述中使用了手指,触控笔也是一样的。
- pin 全称是 pointerIndex,表示第几个手指。(pin < getPointerCount() )
- pos 表示历史数据中的第几个数据。( pos < getHistorySize() )
注意:
1、获取接触面积大小和获取压力大小是需要硬件支持的。
2、非常不幸的是大部分设备所使用的电容屏不支持压力检测,但能够大致检测出接触面积。
3、大部分设备的 getPressure()
是使用接触面积来模拟的。
4、由于某些未知的原因(可能系统版本和硬件问题),某些设备不支持该方法。