在android中,事件主要包括点击、长按、拖曳、滑动等操作,这些构成了Android的事件响应,总体来说,所有的事件都由如下三个部分作为基础构成:
按下(action_down),移动(action_move),抬起(action_up)。各种响应归根结底都是基于View以及ViewGroup的,这两者中响应的方法分别有:
View.java中:
publi boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
ViewGroup.java中
public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
public boolean onInterceptTouchEvent(MotionEvent event)
在组件嵌套的情况下,对于事件的响应处理会从最顶层的组件不断向子组件传递,一直到最后的View组件。
可以看到ViewGroup中比View中多出了一个onInterceptTouchEvent方法,这是因为ViewGroup组件可以存在子组件,因此需要通过Intercept判断是否
将该事件传递到子组件中。
函数的具体功能如下:
onTouchEvent是真正用来进行业务逻辑处理的地方,返回true表示已经将该事件消费,返回false表明事件继续传递。
onInterceptTouchEvent是用来进行判断是否需要对事件进行拦截从而阻止其继续往子组件传递的,返回false表示无需拦截,则递归的调用子组件的dispatchTouchEvent
方法;返回true表示需要拦截,则直接调用本组件的onTouchEvent方法进行处理。
以上两个的功能相对好理解一些,最主要的是第三个,之前在网上看了很多,但是都没有讲的特别清楚的。大都说是用于事件分发的,返回true表示不继续分发,返回false表
示继续分发。但是一直没讲明白这个跟采用onInterceptTouchEvent的区别在哪里。。
直接点说,Android对于touch事件的处理是通过递归来进行的,而这种递归就体现在dispatchTouchEvent上。以上所写的两个函数就是在dispatchTouchEvent中被调用并
且执行从而实现其分发的业务逻辑的。
在dispatchTouchEvent中有可能会调用三个方法:
1、本组件的onInterceptTouchEvent
2、子组件的dispatchTouchEvent
3、本组件的onTouchEvent
ViewGroup中dispatchTouchEvent()具体的执行逻辑:
1、首先执行本组件的onInterceptTouchEvent。如果返回false,表明无需拦截,则调用第二个方法,即子组件的dispatchTouchEvent方法;如果返回true,无需向子组件
传递,则直接调用本组件的onTouchEvent方法
2、第一步中如果需要向子组件传递事件。如果递归调用子组件的dispatchTouchEvent返回false,则调用本组件的onTouchEvent方法;如果返回true,则无需调用本组件的
onTouchEvent方法
3、根据前两步的执行结果,将该dispatchTouchEvent的返回值返回给父组件的dispatchTouchEvent方法。
view中的dispatchTouchEvent会直接调用其自身的onTouchEvent。
一般没有必要重写dispatchTouchEvent方法,如果一定要重写,请注意调用super.dispatchTouchEvent()方法,否则递归调用到此处即停止。
在不考虑dispatchTouchEvent的情况下,简单的执行流程是这样的:
最顶层的组件首先响应事件,然后不断向子组件进行传递,调用子组件的onInterceptTouchEvent方法,一直到某个组件A的onInterceptTouchEvent
方法返回true或者到达了view组件,然后调用该组件的onTouchEvent方法,之后不断向父组件进行返回,调用父组件的onTouchEvent直到某个父组件
的onTouchEvent方法返回true。
其实就是个首先从父组件不断向下调用onInterceptTouchEvent,然后从子组件不断向上调用onTouchEvent的过程。
还需要注意的:
1、假如这个过程中某个组件截获并处理了ACTION_DOWN事件,则之后相应的ACTION_MOVE、ACTION_UP等其他事件将不再会被传递到他的子孙组
件,而是传递到该组件后就执行返回的流程。
2、如果某个组件的onInterceptTouchEvent对ACTION_DOWN返回true,则之后的ACTION_MOVE,ACTION_DOWN等将不会再执行本onInterceptTouchEvent,
而是直接传递给本组件的onTouchEvent,但是依然会经过其父组件的onInterceptTouchEvent。
MotionEvent事件对象
一般我们是在View的onTouchEvent方法中处理MotionEvent对象的.
public boolean onTouchEvent(MotionEvent event)
在这里我们需要从一个MotionEvent对象中获得哪些信息呢?
(1)首先应该是事件的类型吧?
可以通过getAction(),在android2.2之后加入多点触控支持之后使用getActionMasked()方法.
这两个方法的区别见后文.
主要的事件类型有:
ACTION_DOWN: 表示用户开始触摸.
ACTION_MOVE: 表示用户在移动(手指或者其他)
ACTION_UP:表示用户抬起了手指
ACTION_CANCEL:表示手势被取消了,一些关于这个事件类型的讨论见:http://stackoverflow.com/questions/11960861/what-causes-a-motionevent-action-cancel-in-android
还有一个不常见的:
ACTION_OUTSIDE: 表示用户触碰超出了正常的UI边界.
但是对于多点触控的支持,Android加入了以下一些事件类型.来处理,如另外有手指按下了,
有的手指抬起来了.等等:
ACTION_POINTER_DOWN:有一个非主要的手指按下了.
ACTION_POINTER_UP:一个非主要的手指抬起来了
(2)事件发生的位置,x,y轴
getX() 获得事件发生时,触摸的中间区域在屏幕的X轴.
getY() 获得事件发生时,触摸的中间区域在屏幕的X轴.
在多点触控中还可以通过:
getX(int pointerIndex) ,来获得对应手指事件的发生位置. 获得Y轴用getY(int pointerIndex)
(3)其他属性
getEdgeFlags():当事件类型是ActionDown时可以通过此方法获得,手指触控开始的边界. 如果是的话,有如下几种值:EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM