在自定义 View 的过程中,如果设计到控件的触摸事件处理,我们就需要重写 onTouchEvent()
方法。在这个方法中最重要的一个类就是 MotionEvent
类。下面会详细介绍一下这个类的各种说明。
获取位置坐标的几个方法
在自定义 View 中,有 View.getLeft()
、View.getRight()
、View.getTop()
、View.getBottom()
等方法,它们的值是本身相对于父控件的距离,和屏幕没有关系。
我们都知道,每个触摸事件都代表用户在屏幕上的一个动作,而每个动作必定有其发生的位置。在 MotionEvent 中就有一系列与标触摸事件发生位置相关的函数:
-
getX()
/getY()
:表示触摸点相对于控件本身最左边和最上边的距离——相对坐标; -
getRawX()
/getRawY()
:表示触摸点相对于屏幕的坐标——绝对坐标。
事件类型
MotionEvent 对象是与用户触摸相关的时间序列,该序列从用户首次触摸屏幕开始,经历手指在屏幕表面的任何移动,直到手指离开屏幕时结束。手指的初次触摸(ACTION_DOWN操作),滑动(ACTION_MOVE操作)和抬起(ACTION_UP)都会创建 MotionEvent 对象,每次触摸时候这三个操作是肯定发生的。
MotionEvent.ACTION_DOWN
:当屏幕检测到第一个触点按下之后就会触发到这个事件;
MotionEvent.ACTION_MOVE
:当触点在屏幕上移动时触发,触点在屏幕上停留也是会触发的,主要是由于它的灵敏度很高,而我们的手指又不可能完全静止(即使我们感觉不到移动,但其实我们的手指也在不停地抖动) ;
MotionEvent.ACTION_POINTER_DOWN
:当屏幕上已经有触点处于按下的状态的时候,再有新的触点被按下时触发;
MotionEvent.ACTION_POINTER_UP
:当屏幕上有多个点被按住,松开其中一个点时触发(即非最后一个点被放开时)触发;
MotionEvent.ACTION_UP
:当触点松开时被触发;
MotionEvent.ACTION_CANCEL
:不是由用户直接触发,由系统在需要的时候触发,例如当父 view 通过使函数 onInterceptTouchEvent() 返回 true,从子 view 拿回处理事件的控制权时,就会给子 view 发一个 ACTION_CANCEL 事件,子 view 就再也不会收到后续事件了。
其它相关方法
getAction ()
:返回动作类型 ;
getActionMasked()
:多点触控获取经过掩码后的动作类型;
getActionIndex()
:多点触控获取经过掩码和平移后的索引;
getSize()
:指压范围;
getPressure()
: 压力值,0-1之间,看情况,很可能始终返回1,具体的级别由驱动和物理硬件决定的 ;
getEdgeFlags()
:当事件类型是 ActionDown 时可以通过此方法获得边缘标记(EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM)
,但是看设备情况,很可能始终返回 0;
getDownTime()
:按下开始时间;
getEventTime()
: 事件结束时间;
getPointerCount()
:获取触控点的数量,比如 2 则可能是两个手指同时按压屏幕;
getPointerId(nID)
:对于每个触控的点的细节,我们可以通过一个循环执行 getPointerId 方法获取索引;
Pointer
为了可以表示多个触摸点的动作,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 ,然后使用 findPointerIndex(int) 来获得其 index 值,然后再获得其他信息。
getAction 和 getActionMasked
一个 MotionEvent 对象中可以包含多个触摸点的事件。当 MotionEvent 对象只包含一个触摸点的事件时,上边两个函数的结果是相同的,但是当包含多个触摸点时,二者的结果就不同啦。
getAction 获得的 int 值是由 pointer 的 index 值和事件类型值组合而成的,而 getActionWithMasked 则只返回事件的类型值。
getAction() return 0x0105.
getActionMasked() will return 0x0005
其中 0x0100 就是 pointer 的 index 值。
触摸事件onTouch/onTouchEvent
设置触摸事件有两种方式,一种是委托式,一种是回调式。
第一种就是将事件的处理委托给监听器处理,你可以定义一个 View.OnTouchListener 接口的子类作为监听器,实现它的 onTouch() 方法,onTouch 方法接收一个 MotionEvent 参数和一个 View 参数。
第二种是重写 View 类(在 Android 中任何一个控件和 Activity 都是间接或者直接继承于 View)自己本身的 onTouchEvent 方法,也就是控件自己处理事件,onTouchEvent 方法仅接收 MotionEvent 参数,这是因为监听器可以监听多个 View 控件的事件。
或者也可以这样写,自定义 View 实现 OnTouchListener 接口,控件自己处理事件。
还有更多手势操作(双击、长按、滑动、滚动)、多点触控等概念。
参考文章:
Android MotionEvent详解
Android的MotionEvent和事件处理
本文章只是作为自己学习和总结所用,如有使用不当之处可随时@我,谢谢