Android onInterceptTouchEvent 和 onTouchEvent

onInterceptTouchEvent方法是ViewGroup的方法,也就是所有的容器都将继承该方法。此方法设计的意图是为了在父容器将touch事件传递给子控件的时候可以统一控制。比如button就没有该方法,很明显,button不可能拥有子控件。

onTouchEvent方法是view的方法,当该控件接收到touch事件时,此方法会被执行。每一个touch事件总是从ACTION_DOWN事件开始,ACTION_UP结束。Touchevent 中,返回值是 true ,则说明消耗掉了这个事件,返回值是 false ,则没有消耗掉,会继续传递下去

Touch事件几大原则:

1.如果在某个层级没有处理ACTION_DOWN事件,那么该层就再也收不到后续的Touch事件了直到下一次ACTION_DOWN事件。

说明:a.某个层级没有处理某个事件指的是它以及它的子View都没有处理该事件。

b.这条规则不适用于Activity层(它是顶层),它们可以收到每一个Touch事件。

c.如果没有处理ACTION\_MOVE这类事件,不会有任何影响。

2.如果ACTION_DOWN事件发生在某个View的范围之内,则后续的ACTION_MOVE,ACTION_UP和ACTION_CANCEL等事件都将被发往该View,即使事件已经出界了。

3.第一根按下的手指触发ACTION_DOWN事件,之后按下的手指触发ACTION_POINTER_DOWN事件,中间起来的手指触发ACTION_POINTER_UP事件,最后起来的手指触发ACTION_UP事件(即使它不是触发ACTION_DOWN事件的那根手指)。

4.pointer id可以用于跟踪手指,从按下的那个时刻起pointer id生效,直至起来的那一刻失效,这之间维持不变。

5.如果一个ACTION_DOWN事件被父View拦截了,则任何子View不会再收到任何Touch事件了(这符合第1点的要求)。

6.如果一个非ACTION_DOWN事件被父View拦截了,则那些上次处理了ACTION_DOWN事件的子View会收到一个ACTION_CANCEL事件,之后不会再收到任何Touch事件了,即使父View不再拦截后续的Touch事件。

7.如果父View决定处理Touch事件或者子View没有处理Touch事件,则父View按照普通View的处理方式处理Touch事件,否则它根本不处理Touch事件(它只负责分发)。

8.如果父View在onInterceptTouchEvent中拦截了事件,则onInterceptTouchEvent中不会再收到Touch事件了,事件被直接交给它自己处理(按照普通View的处理方式)。

从手指开始触摸屏幕开始,首先得到touch事件的必然是最外层的容器,此时作为父容器,其首先触发的是onInterceptTouchEvent,该方法提供一次可以拦截子控件的触摸事件的机会。同样的返回true表示消费了该事件,此时该事件会传递至该容器的onTouchEvent方法。

如果返回false,那么则继续传递给在该触摸位置的子控件(比如一个RelativeLayout),如果子控件也不处理则继续传递给最后一个子控件,当最后一个子控件依然返回false,那么该事件则冒泡传递给父控件,(冒泡想必写过js的同学很熟悉)。

More:

  1. 一个事件必然从ACTION_DOWN开始,所以当某一个控件处理了一个ACTION_DOWN事件,可以理解直到下一个ACTION_DOWN之前所有的触摸事件都应当由该控件(target)进行处理。

  2. 对于各种listener,显然是由android系统进行处理的。所以Super.onTouchEvent()应当放在子类的该函数的第一行,可以优先保证系统的方法不会被自定义的操作截取而导致没有机会执行。

  3. 牢记一个原则,所有的touch事件都是从父容器开始向下传递的,呈U字形

(1) 父容器(类型为 <? Extends ViewGroup>) onInterceptTouchEvent接收到事件,如果return false,转步骤(2),否则转 步骤(3)

(2) 父容器在该位置(ACTION_DOWN发生的位置)存在子控件,如果子控件类型为

<? Extends ViewGroup>,此时该子控件成为父容器,转步骤(1),如果子控件为普通view,即 不是viewgroup的子类,使用子控件转步骤(3),如果不存在子控件,使用父容器转步骤(3)

(3) 该touch事件交给该控件的onTouchEvent进行响应,如果return false,当其存在父容器时,事件传递给父容器,转步骤(3)。当不存在父容器时,该事件被丢弃。如果return true,表示事件被消费了,此touch事件终止。