WebView与ListView滑动冲突——(一)事件基础篇
Android中的事件有很多种,从宏观上来讲我们最常见,最常用的就是onClick事件了,微观上来说就是onTouchEvent、onInterceptTouchEvent、dispatchTouchEvent等方法都具有MotionEvent参数,该Event即为用户一次行为的具体体现。如果其中某一个方法返回true,则代表改方法消费了该事件,则该事件将不会向下传递,其后的ACTION_MOVE、ACTION_UP等事件都将会在这里进行处理,不会向下传递。
MotionEvent 常见事件:
常量 | 解释 |
---|---|
ACTION_DOWN | 表示用户开始触摸 |
ACTION_MOVE | 表示用户在移动 |
ACTION_UP | 表示用户抬起了手指 |
ACTION_CANCEL | 表示手势被取消了,不常见 |
ACTION_OUTSIDE | 表示用户触碰超出了正常的UI边界,不常见 |
ACTION_POINTER_DOWN | 有一个非主要的手指按下了(多点按下动作) |
ACTION_POINTER_UP | 一个非主要的手指抬起来了(多点离开动作) |
事件分发机制
Android的事件分发机制主要涉及到这三个方法:dispatchTouchEvent、onInterceptTouchEvent以及onTouchEvent
1.dispatchTouchEvent
处理触摸事件的分发,Android中的所有事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。
2.onInterceptTouchEvent
该方法只有在ViewGroup或其子类中才具有。作用是系统向该ViewGroup及其各个ChildView触发onTouchEvent方法之前对相关事件进行一次拦截。 ACTION_DOWN事件会首先传递到ViewGroup中的onInterceptTouchEvent方法中,下面分两种情况来说明一下该方法返回值的作用。
返回true
如果onInterceptTouchEvent处理完ACTION_DOWN事件返回true,则系统不会将该ACTION_DOWN事件传到目标View中的onTouchEvent方法中,而且后续的ACTION_MOVE、ACTION_UP事件将不再调用该ViewGroup中的onInterceptTouchEvent方法,直接将这些事件传递给该ViewGroup中的onTouchEvent处理。
如果onInterceptTouchEvent处理完ACTION_MOVE返回true,则系统不会将ACTION_MOVE事件传递到目标View中的onTouchEvent方法中,而是传递ACTION_CANCEL事件到目标View的onTouchEvent方法中,只传递一次,此后与目标View没有任何关系(包括最后的ACTION_UP事件),只调用ViewGroup中的onTouchEvent方法。
返回false
如果onInterceptTouchEvent处理完ACTION_DOWN事件返回false,则系统还会将该ACTION_DOWN事件传到目标View当中,并且后续的ACTION_MOVE事件同样先传递到onInterceptTouchEvent中
如果onInterceptTouchEvent处理完ACTION_MOVE返回false,则系统还会将ACTION_MOVE事件传递到目标View中的onTouchEvent方法中。
3.onTouchEvent
对于onTouchEvent也分两种情况来说明把,毕竟这种方法的返回值才是关键。
返回true
如果View中的onTouchEvent返回true的话就表示该View消费了事件,进入不到ViewGroup的onTouchEvent中了
如果ViewGrop的onTouchEvent返回true的话就表示该ViewGrop消费了事件,进入不到activity的onTouchEvent中了
返回false
View的onTouchEvent返回为false表示view处理完onTouchEvent后不消费这次事件,那么这个事件就会继续传递到他的上一层ViewGroup的onTouchEvent事件中
ViewGrop的onTouchEvent返回为false表示这个ViewGroup处理完onTouchEvent后不消费这次事件,这个事件就会继续传递到activity的onTouchEvent中
总之,如果最里层的返回false就会交给他的上一层处理,否则就会消费这次事件,停止向下传递
ViewGroup事件分发模型
想要搞清楚ViewGroup的事件分发机制,首先就得先在脑子中行程一种宏观上的事件模型,才能大致明白事件分发到底是个什么东西。如图:
我们就先通过ViewGroup的dispatchTouchEvent来了解一下事件分发模型。当一个Touch事件(触摸事件为例)到达根节点,即Acitivty的ViewGroup时,它会依次下发,下发的过程是调用子View(ViewGroup)的dispatchTouchEvent方法实现的。简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,而当子View为ViewGroup时,又会通过调用ViwGroup的dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。
上述例子中的消息下发顺序是这样的:①-②-③-⑤-⑥-⑦-④。dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,那么⑥-⑦-④将都接收不到本次Touch事件。
图解事件分发
看到这相信你已经对事件分发有了大致的了解。下面通过几张图来学习一下dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent这三个方法和MotionEvent是怎么协同工作的。
ACTION_DOWN都未被消费
- 都不拦截,ACTION_DOWN会依次向下传递
- 都不消费,onTouchEvent会依次向上传递
- 后续的ACTION_MOVE和ACTION_UP都不会在被传递
- dispatchTouchEvent会返回false,表示事件未派发出去(未被消费)
ACTION_DOWN被View消费
- ACTION_DOWN将不会向下传递
- 之前消费了ACTION_DOWN事件,不管我后面消费不消费,ACTION_MOVE和ACTION_UP都会由我来处理(只要不被上层拦截)
- 如果该View消费了ACTION_MOVE或ACTION_UP,则上层的dispatchTouchEvent将返回true,反之则返回false
ACTION_DOWN被View消费,上层拦截剩下事件
- ACTION_DOWN被View消费后。上层的onInterceptTouchEvent返回true表示拦截事件
ACTION_DOWN直接被上层拦截
- 一开始就被上层ViewGroup所拦截,并且消费了ACTION_DOWN事件,则后面的ACTION_MOVE和ACTION_UP都会被传递给他,不会向下传递给子View
注:Android中的Touch事件都是从ACTION_DOWN开始的
- 单手指操作:ACTION_DOWN—ACTION_MOVE—-ACTION_UP
- 多手指操作:ACTION_DOWN—ACTION_POINTER_DOWN—ACTION_MOVE–ACTION_POINTER_UP—ACTION_UP