在上篇文章中,我们分析了Android TouchEvent是如何从底层传递给一个Activity之后,接下来继续分析TouchEvent是如何在Activity中各个View之间进行传递的。

View和ViewGroup

每一个UI元素都是View的子类,例如TextView,ImageView等

每一个Layout布局都是ViewGroup的子类,例如FragmentLayout,LinearLayout,而ViewGroup本身也是View的一个子类,但它是多个View的容器

Android系统中的每个View的子类都具有下面三个和TouchEvent处理密切相关的方法:

  • public boolean dispatchTouchEvent(MotionEvent ev)

    这个方法用来向下分发TouchEvent,如果返回true,则表示事件已经被处理,不会再向下分发

  • public boolean onInterceptTouchEvent(MotionEvent ev)

    这个方法用来在ViewGroup中拦截TouchEvent,View没有这个方法,只在ViewGroup中才有该方法。

    • down事件首先会传递到onInterceptTouchEvent()方法
    • 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,且不会再向下分发事件。
    • 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么该事件会向下分发,且后续的move, up等事件还会先传递给该ViewGroup的onInterceptTouchEvent()

      总而言之就是return true拦截事件,不向下分发;return false不拦截事件,向下分发

  • public boolean onTouchEvent(MotionEvent ev)

    这个方法在View的子类中用来处理TouchEvent

    • 如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
    • 如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理

下面,我们来看一个Demo。如下图,有三个Layout:Out、Middle、Center,Out是最外层的一个Layout

Demo1

  • 测试1:三个Layout中只有Center是Clickable的,并点击Center,其TouchEvent的传递流程如下图所示

Demo2

首先触摸事件发生时(ACTION_DOWN),由系统调用Activity的dispatchTouchEvent方法,分发该事件。根据触摸事件的坐标,将此事件传递给out的dispatchTouchEvent处理,out则调用onInterceptTouchEvent 判断事件是由自己处理,还是继续分发给子View。此处由于out不处理Touch事件,故根据事件发生坐标,将事件传递给out的直接子View(即middle)。

Middle及Center中事件处理过程同上。但是由于Center组件是clickable 表示其能处理Touch事件,故center中的onInterceptTouchEvent方法将事件传递给center自己的onTouchEvent方法处理。至此,此Touch事件已被处理,不继续进行传递。

Move和up事件处理流程类似,但是再center内的dispatchTouchEvent方法内被直接分配给onTouchEvent处理,不需经过onInterceptTouchEvent判断。这是由于,android系统中将1个down事件、n个move事件、1个up事件整体作为一次逻辑上的触控操作,Down事件已经确定了处理事件的对象,则后续的move、up事件也确定了处理事件的对象。

  • 测试2:三个Layout中都不是Clickable的,其TouchEvent的传递流程如下图所示

Demo3

事件处理流程大致同上,区别是此状态下,所有组件都不会处理事件,事件并不会被center的onTouchEvent方法“消费”,则事件会层层逆向传递,直至回到Activity,若Activity也不对此事件进行处理,此事件相当于消失了(无效果)。

对于后续的move、up事件,由于第一个down事件已经确定由Activity处理事件,故up事有由Activity的dispatchTouchEvent直接分发给自己的onTouchEvent方法处理。

注意,Activity的dispatchTouchEvent()和onTouchEvent()是由DecoView实现回调的,而Activity中是没有onInterceptTouchEvent()这个方法的,它被DecoView自己消费掉了。

原创不易,欢迎转载,但还请注明出处:waynell.github.io