在日常开发过程中,可能会遇到这些问题:滑动冲突、点击事件响应冲突等问题。那么造成这些问题的根源到底是什么呢?其实这都是Android事件分发导致的,只有掌握了事件分发机制,才能让我们从根源上理解并解决这类问题。
事件分发过程中,涉及到三种UI对象类型:Activity、ViewGroup、View 及其派生类。三者之间的关系如下图:
发生一次点击事件时,事件会按照Activity->ViewGroup->View的顺序,进行事件传递。
Android事件分发机制,其实就是Activity、ViewGroup、View三者对触摸点击事件的事件传递过程。
事件分发流程图
在整个事件分发,并响应事件的过程中,有三个重要的方法:
Activity中主要涉及以下两个事件方法:
在Activity中接受到点击事件,首先会执行dispatchTouchEvent()方法,进行事件分发。经过window、decorView依次传递后,页面上的 ViewGroup会接收到该事件。ViewGroup如果消费了该事件,则分发结束(流程在ViewGroup中继续向下分发),未消费则继续调用Activity的onTouchEvent 方法处理事件,流程图如下:
Activity事件分发流程
ViewGroup 涉及到三个事件分发与处理的方法:
ViewGroup事件分发流程图
ViewGroup通过dispatchTouchEvent()方法接收到事件,然后根据ViewGroup onInterceptTouchEvent()方法的返回值判断:
View 主要涉及如下两个事件分发与处理的方法:
View事件分发流程图
View通过dispatchTouchEvent方法接收到从ViewGroup传递过来的事件后,直接调用 onTouchEvent方法处理事件。如果没有消费事件,则调用ViewGroup的onTouchEvent方法处理事件,然后继续ViewGroup事件流程;如果消费了该事件,则分发结束。
注意:
在View把事件消费后,如果View的onTouch方法返回true,View的dispatchTouchEvent方法会直接返回true,不会再调用View的onClick方法;只有当onTouch方法返回false时,才会有onClick事件处理。
这两个方法都是在View的dispatchTouchEvent中调用,但onTouch优先于onTouchEvent执行。如果在onTouch方法中返回true将事件消费掉,onTouchEvent()将不会再执行。
特别注意:请看下面代码
//1. mOnTouchListener的值不能为空
//2. 当前点击的控件必须是enable的
mOnTouchListener !=null && (mViewFlags & ENABLED_MASK) == ENABLED &&mOnTouchListener.onTouch(this, event)
因此如果你有一个控件是非enable的,那么给它注册onTouch事件将永远得不到执行。对于这一类控件,如果我们想要监听它的touch事件,就必须通过在该控件中重写onTouchEvent方法来实现。
接收了ACTION_DOWN事件的函数不一定能收到后续事件(ACTION_MOVE、ACTION_UP);如果在某个对象(Activity、ViewGroup、View)的dispatchTouchEvent()消费事件(返回true),那么收到ACTION_DOWN的函数也能收到ACTION_MOVE和ACTION_UP。