Android事件分发
事件传递,实际上传递的是MotionEvent对象。
MotionEvent
该类的对象保存了事件的类型,触摸的坐标,产生的时间等信息
Android事件分发是从父view传递到子view的。其中的关键是dispatchtouchEvent、onInterceptTouchEvent和onTouchEvent方法,所以先简单介绍这三个方法。
dispatchTouchEvent
当发生触摸事件传递必定会调用这个方法,dispatchTouchEvent方法会把事件往目标子view传递。方法返回true则不再传递,该方法自己消费事件;返回false则不再向下传递,而是把事件传到父view的onTouchEvent方法;返回super.dispatchTouchEvent则调用自己的onInterceptTouchEvent;(本文默认dispatchTouchEvent方法返回true或false的情况下,方法内部将不在调用super.dispatchTouchEvent,否则事件传递流程将与本文介绍的不一致,因为super.dispatchTouchEvent方法内会继续处理事件的分发)
onInterceptTouchEvent
这个方法是ViewGroup特有的,普通View(如TextView、ImageView)是没有的。方法返回true表示当前ViewGroup对事件拦截,事件传到本身的onTouchEvent;返回false则放行,把事件传递给子View的dispatchTouchEvent;
onTouchEvent
这个事件处理的方法。方法返回true则事件不再传递,方法自己消费事件;返回false则把事件传到父view的onTouchEvent方法;返回super.onTouchEvent也会事件传到父view的onTouchEvent方法。该方法在View的dispatchTouchEvent方法内部调用。如果一个view设置了OnclickListener,就会在它的内部调用OnClick方法。
ACTION_DOWN事件
Android ACTION_DOWN事件的传递是从父View一层一层的通过dispatchTouchEven发方法往子View传递的,如果传递途中都不对事件处理,又会一层一层的从子view通过onTouchEvent方法传到父View。下图是ACTION_DOWM事件分发的一个流程。按照默认的流程,传递过程的每一个View都不消费事件,完整的流程就是Activity.dispatchTouchEvent -> ViewGroup.dispatchTouchEvent -> ViewGroup.onIntercepteTouchEvent -> View.dispatchTouchEvent -> View.onTouchEvent -> ViewGroup.onTouchEvent -> Activity.onTouchEvent。
ACTION_MOVE、ACTION_UP事件
ACTION_MOVE、ACTION_UP事件分发和ACTION_DOWM略有不同,这两个事件的分发流程受ACTION_DOWN事件的影响。在Android事件分发的过程中,如果一个View对ACTION_DOWN事件不做处理,那么这个view将不会再接收到ACTION_MOVE、ACTION_UP事件。如下图所示,ACTION_DOWM事件分发到最下面View,View并没有对事件做处理,而 ViewGroup B的onTouchEvent返回true表示对事件做了处理。那么后续的ACTION_MOVE、ACTION_UP事件将不会再传给最下面的View,而是直接通过ViewGroup B的dispatchTouchEvent方法传到了onTouchEvent方法(红线表示ACTION_MOVE、ACTION_UP事件,黑线表示ACTION_DOWN事件)。同时,也可以看到事件传递到ViewGroupB onTouchEvent方法就终止了,因为它返回true,已经消费了事件就不会再往上传递了。如果整个流程都不处理ACTION_DOWN事件,那么ACTION_MOVE和ACTION_UP只会从Activity.dispatchTouchEvent方法传递到Activity.onTouchEvent方法。
OnTouchListener和OnClickListener
view设置了OnTouchListener,那么事件传递给onTouchEvent之前,都会先调用onTouch()方法。如果onTouch返回true,则事件被消费,不再传递;返回false表示onTouch()不做处理,事件传递给onTouchEvent,然后继续传递下去。(onClick方法是在onTouchEvent内部调用的,所以这就是为什么onTouch返回true之后,onClick方法就不会被调用 )
view设置了OnClickListener,那么onTouchEvent方法就返回true,表示消费了事件。原因是在设置点击事件的时候view的clickable被设置为true,如果一个view的clickable为true,那么方法onTouchEvent就返回true(注意,有些控件clickable默认为true,如Button的clickable默认为true,TextView的clickable默认为false)。同理设置OnLongClickListener同样会导致onTouchEvent方法返回true。
所以这几个方法的调用顺序是onTouch -> onTouchEvent -> onClick
小结
事件分发的流程主要通过dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent方法处理,其中onInterceptTouchEvent方法只有ViewGroup才有。下篇博客将从源码分析事件分发机制。