Android触屏事件和MotionEvent详解

  Android屏幕操作

  屏幕是用户和Android设备交互的主要媒介,屏幕分为触屏和非触屏。Android设备目前有四种类型:Android Phone,Android Tablet,Android Wear和Android TV。Android TV大都使用非触屏,其他三类设备则大都使用触屏。对非触屏设备,用户可以通过键盘鼠标或遥控器在屏幕上操作。对触屏设备,用户主要通过手指或触控笔等工具在屏幕上操作,当然也可以通过外接的键盘,鼠标和轨迹球等工具来操作。

  Android屏幕交互事件

  用户在设备屏幕上的所有操作都会转换为各类屏幕交互事件。Android屏幕交互事件主要有如下几种类型。

  为了方便理解和简化描述,后文在介绍时会统一用手指操作来代指所有的触屏操作。例如“当手指接触屏幕时产生此事件”,并不表示只能用手指接触屏幕才会产生此事件,而是需要理解为“当手指,触控笔等工具接触屏幕时都会产生此事件”。

  触屏事件类型

  按照动作来分,可以将触屏事件可以分为以下三类

  其中手指按到屏幕上和手指离开屏幕一定是成对出现的,在这中间会出现不定次数的手指在屏幕上移动的事件。

  触屏事件序列

  在Android系统中,从手指按到屏幕上开始,到手指离开屏幕,这个过程中产生的一系列触屏事件构成了一个事件序列(也可以称为事件流)。对多点触屏事件,则是从第一个手指按到屏幕上开始,到最后一个手指离开屏幕为止。

  一个触屏事件序列第一个事件一定是手指按到屏幕上,最后一个事件一定是手指离开屏幕。用户在设备屏幕上的所有触屏操作最终都会转换为若干个这样的事件序列。

  理解触屏事件序列的概念非常重要,Android中对触屏事件的处理很多时候需要以事件序列为单位进行考察。

  Android触屏事件在代码中的表示

  在Android系统中使用MotionEvent对象来表示一个触屏事件,当用户用手指在屏幕上操作时,会产生一系列的MotionEvent对象。但是需要注意的是,产生了一个MotionEvent对象并不表示这一定是一个触屏操作,MotionEvent不仅可以用来表示touch event,还可以表示hover event,scroll event。也就是说,除了key event之外的其他屏幕交互事件都用MotionEvent来表示(key event用KeyEvent对象表示)。

  在MotionEvent类中将产生此次事件的动作称为motion,将产生此动作的主体(如手指,鼠标等)称为pointer。一个MotionEvent对象中可以包含一个或多个pointer,每个pointer都包含id,index,位置,大小,方向等属性。在一个触屏事件序列的多个事件中,同一个pointer拥有相同的id,但是index可以不同。

  这里只讨论MotionEvent中关于touch event的部分。在MotionEvent对象中主要包含了如下信息:

  1.操作类型(action code)

  MotionEvent提供了getActionMasked()方法来获取此次操作的类型,它是一个int型数值。除了getActionMasked()外还有一个getAction()方法,它和getActionMasked()的区别会在后面介绍。

  在MotionEvent类中定义了一系列的int常量来表示各种预定义的操作类型。列举如下。

  事件类型常量

  含义说明

  ACTION_DOWN

  当手指接触屏幕时产生此事件,在多点触摸时,只有第一个手指接触屏幕时才会产生此事件,中间其他手指接触屏幕不会产生此事件。它表示一个触屏事件序列的开始。

  ACTION_UP

  当手指离开屏幕时产生此事件,在多点触摸时,只有最后一个手指(这个手指并不一定是产生ACTION_DOWN事件的那个手指)离开屏幕时才会产生此事件,中间其他手指离开屏幕不会产生此事件。它表示一个触屏事件序列的结束。

  ACTION_MOVE

  当手指在屏幕上滑动时产生此事件, 在多点触摸时,每个手指的滑动都会产生一个此事件

  ACTION_POINTER_DOWN

  只有在多点触摸时才会产生此事件,在一个触屏事件序列中,除第一个接触屏幕的手指外,其他手指接触屏幕时会产生此事件。

  ACTION_POINTER_UP

  同样只有在多点触摸时才会产生此事件,在一个触屏事件序列中,除最后一个离开屏幕的手指外,其他手指离开屏幕时会产生此事件。

  ACTION_CANCEL

  这个事件比较特殊,它和上述事件都不一样,上述事件都是由用户在屏幕上操作所触发的,但是这个事件是由系统自动产生的。当一个事件序列需要提前终止的时候由系统自动产生此事件。正常来说,一个事件序列应该以最后一个手指离开屏幕,也就是ACTION_UP作为结束,但是在某些情况下,事件序列需要被提前终止。这通常是因为处理这个事件序列的View对象的Parent对象在事件序列结束之前主动拦截了后续的事件。此外,如果处理这个事件序列的View对象从窗口中被移除了,它也会收到ACTION_CANCEL事件。例如处理这个事件序列的View对象所在的Activty被finish(),所在的Dialog被dismiss(),或者被其Parent View Remove了。在这些情况下,虽然这时手指还停留在屏幕上,但View对象将无法再接收到后续的触屏事件,这时它会收到ACTION_CANCEL事件,表示事件序列由于外在原因需要提前终止。

  结合上面触屏事件序列的描述可以知道,一个正常的触屏事件序列一定是以ACTION_DOWN为开始,以ACTION_UP为结束,中间可以有0个或多个ACTION_MOVE, 如果是多点触摸,中间还会有若干次的ACTION_POINTER_DOWN和ACTION_POINTER_UP。ACTION_POINTER_DOWN和ACTION_POINTER_UP一定是数量相对的。

  一个提前终止的触屏事件序列一定是以ACTION_DOWN为开始,以ACTION_CANCEL为结束,中间可以有0个或多个ACTION_MOVE, 如果是多点触摸,中间还会有若干次的ACTION_POINTER_DOWN和ACTION_POINTER_UP。ACTION_POINTER_DOWN和ACTION_POINTER_UP的数量可能不同。

  getAction()和getActionMasked()的区别:对ACTION_POINTER_DOWN和ACTION_POINTER_UP之外的事件,getAction()返回值和getActionMasked()是相同的。对ACTION_POINTER_DOWN和ACTION_POINTER_UP,getAction()返回值和getActionMasked()返回值稍有不同。getAction()返回值包含了操作类型和产生此事件的pointer对应的pointer index两个信息,其中低8位代表操作类型,高8位代表pointer index 。

  2.pointer信息

  除此之外,还有getToolMajor(),getToolMinor(),getTouchMajor(),getTouchMinor(),getOrientation()等方法获取pointer的区域大小,方向等信息。由于实际使用的较少,这里就不做介绍了。

  3.操作时间

  可以通过MotionEvent类的getEventTime()方法来获取此事件产生的时间。

  4.事件序列的历史数据

  在MotionEvent对象中还会保存其所在的事件序列的一些历史事件的信息,可以通过getHistorySize()获取历史事件记录的条数,通过一系列的getHistoricalXXX()方法获取历史事件的信息。由于ACTION_DOWN 是一个事件序列的开始,所以ACTION_DOWN对应的事件对象中是不会有历史事件记录的,在这之后的事件对应的MotionEvent对象中会有0到多个的历史事件信息的记录,具体记录的个数并不固定,总的数量也不会太多。

  在上述信息中,使用比较多的是前两条,也就是事件的类型和事件产生时pointer的相关信息。

  如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

  您可能感兴趣的文章: