有些view的实现会自定义onInterceptTouchEvent, 以及OnTouchEvent两个响应事件,onTouchEvent的功能我们都知道,但是onInterceptTouchEvent的功能是什么呢?两者又是什么关系?什么情况需要使用?

onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截.

android官方文档有个标准解释,现摘录过来:

首先,看Android的官方文档正解:

onInterceptTouchEvent()与onTouchEvent()的机制:

1. down事件首先会传递到onInterceptTouchEvent()方法

2. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理

3. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。

4. 如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理

5. 如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。

仅仅看这个官方文档解释,就能理解清楚这两个函数关系以及用途的绝对是富有经验的framework高手。

否则,一定需要一个案例来阐释。假设我们有这样一个layout,非常典型的

android:orientation="vertical" android:layout_width="fill_parent"

android:layout_height="fill_parent">

android:orientation="vertical" android:layout_width="fill_parent"

android:layout_height="fill_parent" android:gravity="center">

android:layout_width="wrap_content" android:layout_height="wrap_content"

/>

用一个示例图来解释这个layout:

2019301351-0.jpg?num=0.8800612481381942

通常外围的layoutview1,layoutview2,只是布局的容器不需要响应触屏的点击事件,仅仅Mytextview需要相应点击。但这只是一般情况,一些特殊的布局可能外围容器也要响应,甚至不让里面的mytextview去响应。更有特殊的情况是,动态更换响应对象。

那么首先看一下默认的触屏事件的在两个函数之间的传递流程。如下图:

2019303C6-2.jpg?num=0.14900446476789508

如果仅仅想让MyTextView来响应触屏事件,让MyTextView的OnTouchEvent返回true,那么事件流就变成如下图,可以看到layoutview1,layoutview2已经不能进入OnTouchEvent:

201930M55-4.jpg?num=0.49316220194769616

另外一种情况,就是外围容器想独自处理触屏事件,那么就应该在相应的onInterceptTouchEvent函数中返回true,表示要截获触屏事件,比如layoutview1作截获处理,处理流变成如下图:

201930D40-6.jpg?num=0.7708927846278597

以此类推,我们可以得到各种具体的情况,整个layout的view类层次中都有机会截获,而且能看出来外围的容器view具有优先截获权。

当我们去做一些相对来讲具有更复杂的触屏交互效果的应用时候,经常需要动态变更touch event的处理对象,比如launcher待机桌面和主菜单(见下图),从滑动屏幕开始到停止滑动过程当中,只有外围的容器view才可以处理touch event,否则就会误点击上面的应用图标或者widget.反之在静止不动的状态下则需要能够响应图标(子view)的touch事件。摘取framework中abslistview代码如下public boolean onInterceptTouchEvent(MotionEvent ev) {

int action = ev.getAction();

switch (action & MotionEvent.ACTION_MASK) {

case MotionEvent.ACTION_DOWN: {

if (touchMode == TOUCH_MODE_FLING) {

return true; //fling状态,截获touch,因为在滑动状态,不让子view处理

}

break;

}

case MotionEvent.ACTION_MOVE: {

switch (mTouchMode) {

case TOUCH_MODE_DOWN:

final int pointerIndex = ev.findPointerIndex(mActivePointerId);

final int y = (int) ev.getY(pointerIndex);

if (startScrollIfNeeded(y - mMotionY)) {

return true;//开始滑动状态,截获touch事件,不让子view处理

}

break;

}

break;

}

}

2019303231-8.jpg?num=0.7716262178477136

2019303E7-10.jpg?num=0.24587011646519152

总结:

仅仅通过概览性的官方文档是很难理解onInterceptTouchEvent函数的用途的,只有通过演绎这个抽象的规则,配以图文才能获取这个重要的知识。很显然,默认是返回false,不做截获。返回true之后,事件流的后端控件就没有机会处理touch事件了,把默认的事件流中每个处理函数看作一个节点,这个节点只要返回true, 后续的事件就被截止了,这样想就很好理解。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐