前言

很快乐遇见你~

第二篇文章对源码的剖析较为深刻,不足一个更高的角度来扫视事件散发流程。本文在后面的剖析根底上,对整个事件散发的工作流程进行一个总结,更好地把握事件是如何在不同的对象和办法之间进行传递。

回顾

先来回顾一下整体的流程,以便更好地定位咱们的常识。

  1. 触摸信息从手机触摸屏幕时产生,通过IMS和WMS发送到viewRootImpl
  2. viewRootImpl通过调用view的dispatchPointerEvent办法把触摸信息传递给view
  3. view通过调用本身的dispatchTouchEvent办法开始了事件散发

图中的view指的是一个控件树,他能够是一个viewGroup也能够是一个简略的view。因为viewGroup是继承自view,所以一个控件树,也能够看做是一个view。

咱们明天探讨的工作流程,就是从图中的view调用本身的dispatchTouchEvent开始。

次要对象与办法

事件散发的对象

这一部分内容在第二篇有具体解析,这里做个简略的回顾。

当咱们手机触碰屏幕时会产生一系列的MotionEvent对象,依据触摸的状况不同,这些对象的类型也会不同。具体如下:

· ACTION_DOWN: 示意手指按下屏幕

· ACTION_MOVE: 手指在屏幕上滑动时,会产生一系列的MOVE事件

· ACTION_UP: 手指抬起,来到屏幕、

· ACTION_CANCEL:当出现异常状况事件序列被中断,会产生该类型事件

· ACTION_POINTER_DOWN: 当曾经有一个手指按下的状况下,另一个手指按下会产生该事件

· ACTION_POINTER_UP: 多个手指同时按下的状况下,抬起其中一个手指会产生该事件

事件散发的办法

事件散发属于控件零碎的一部分,次要的散发对象是viewGroup与view。而其中外围的办法有三个: dispatchTouchEventonInterceptTouchEventonTouchEvent 。那么在讲散发流程之前,先来介绍一下这三个办法。这三个办法属于view体系的类,其中Window.CallBack接口中蕴含了 dispatchTouchEventonTouchEvent 办法,Activity和Dialog都实现了Window.CallBack接口,因而都实现了该办法。因这三个办法常常在自定义view中被重写,以下的剖析,如果没有非凡阐明都是在默认办法实现的状况下。

dispatchTouchEvent

该办法是事件散发的外围办法,事件散发的逻辑都是在这个办法中实现。该办法存在于类View中,子类ViewGroup、以及其余的实现类如DecorView都重写了该办法。

无论是在viewGroup还是view,该办法的次要作用都是处理事件。如果胜利解决则返回true,解决失败则返回false,示意事件没有被解决。具体到类,在viewGroup相干类中,该办法的次要作用是把事件散发到该viewGroup所领有的子view,如果子view没有解决则本人解决;在view的相干类中,该办法的次要作用是生产触摸事件。

onInterceptTouchEvent

该办法只存在于viewGroup中,当一个事件须要被散发到子view时,viewGroup会调用此办法查看是否要进行拦挡。如果拦挡则本人解决,而如果不拦挡才会调用子view的 dispatchTouchEvent 办法散发事件。

办法返回true示意拦挡事件,返回false示意不拦挡。

这个办法默认只对鼠标的相干操作的一种非凡状况进行了拦挡,其余的状况须要具体的实现类去重写拦挡。

onTouchEvent

该办法是生产事件的次要办法,存在于view中,viewGroup默认并没有重写该办法。办法返回true示意生产事件,返回false示意不生产事件。

viewGroup散发事件时,如果没有一个子view生产事件,那么会调用本身的onTouchEvent办法来处理事件。View的dispatchTouchEvent办法中,并不是间接调用onTouchEvent办法来生产事件,而是先调用onTouchListener判断是否生产;如果onTouchListener没有生产事件,才会调用onTouchEvent来处理事件。

咱们为view设置的onClickListener与onLongClickListener都是在View的dispatchTouchEvent办法中,依据具体的触摸状况被调用。

重要规定

事件散发有一个很重要的准则:一个触控点的事件序列只能给一个view生产,除非产生异常情况如被viewGroup拦挡 。具体到代码实现就是:生产了一个触控点事件序列的down事件的view,将继续生产该触控点事件序列接下来的所有的事件 。举个栗子:

当我手指按下屏幕时产生了一个down事件,只有一个view生产了这个down事件,那么接下来我的手指滑动屏幕产生的move事件会且仅会给这个view生产。而当我手机抬起,再按下时,这时候又会产生新的down事件,那么这个时候就会再一次去寻找生产down事件的view。所以,事件散发,是以事件序列为单位的

因而上面的工作流程中都是指down事件的散发 ,而不是ACTION_MOVE或ACTION_UP的散发。因为生产了down事件,意味着接下来的move和up事件都会给这个view解决,也就无所谓散发了。但同时留神事件序列是能够被viewGroup的onInterceptTouchEvent中断的,这些就属于其余的状况了。

仔细的读者还会发现事件散发中蕴含了多点触控。在多点触控的状况下,ACTION_POINTER_DOWN与ACTION_DOWN的散发规定是不同的,具体可返回第二篇文章理解具体。ACTION_POINTER_DOWN在ACTION_DOWN的散发模型上稍作了一些批改而已,前面会具体解析,

工作流程模型

工作流程模型,实质上就是不同的控件对象,viewGroup和view之间事件散发办法的关系。须要留神的是,这里探讨的是viewGroup和view的默认办法实现,不波及其余实现类如DecorView的重写办法。

上面用一段伪代码来示意三个事件散发办法之间的关系( 这里再次强调,这里的事件散发模型散发的事件类型是ACTION_DOWN且都是默认的办法,没有通过重写,这点很重要 ):

public boolean dispatchTouchEvent(MotionEvent event){

 // 先判断是否拦挡
 if `(onInterceptTouchEvent()){`
 // 如果拦挡调用本身的onTouchEvent办法判断是否生产事件
 return `onTouchEvent(event);`
 }
 // 否则调用子view的散发办法判断是否处理事件
 if `(childView.dispatchTouchEvent(event)){`
 return true`;`
 `}`else`{`
 return `onTouchEvent(event);`
 }
}

这段代码十分好的展现了三个办法之间的关系:在viewGroup收到触摸事件时,会先去调用 onInterceptTouchEvent 办法判断是否拦挡,如果拦挡则调用本人的 onTouchEvent 办法处理事件,否则调用源码交易子view的 dispatchTouchEvent 办法来散发事件。因为子view也有可能是一个viewGroup,这样就造成了一个相似递归的关系。

这里我再补上view散发逻辑的简化伪代码:

public boolean dispatchTouchEvent(MotionEvent event){

 // 先判断是否存在onTouchListener且返回值为true
 if `(mOnTouchListener!=`null `&& mOnTouchListener.onTouch(event)){`
 // 如果胜利生产则返回true
 return true`;`
 `}`else`{`
 // 否则调用onTouchEvent生产事件
 return `onTouchEvent(event);`
 }
}

view与viewGroup不同的是他不须要散发事件,所以也就没有必要拦挡事件。view会先查看是否有onTouchListener且返回值是否为true,如果是true则间接返回,否则调用onTouchEvent办法来处理事件。

这里为了展现类递归关系应用了画了两个viewGroup,只需看两头一个即可,上面对这个图进行解析:

· viewGroup

· viewGroup的dispatchTouchEvent办法接管到事件音讯,首先会去调用onInterceptTouchEvent判断是否拦挡事件

o 如果拦挡,则调用本身的onTouchEvent办法

o 如果不拦挡则调用子view的dispatchTouchEvent办法

· 子view没有生产事件,那么会调用viewGroup自身的onTouchEvent

· 下面1、2步的处理结果为viewGroup的dispatchTouchEvent办法的处理结果,并返回给上一层的onTouchEvent解决

· view

· view的dispatchTouchEvent默认状况下会调用onTouchEvent来处理事件,返回true示意生产事件,返回false示意没有生产事件

· 第1步的后果就是dispatchTouchEvent办法的处理结果,胜利生产则返回true,没有生产则返回false并交给上一层的onTouchEvent解决

能够看到整个工作流程就是一个“U”型构造,在不拦挡的状况下,会一层层向下寻找生产事件的view。而如果以后view不处理事件,那么就一层层向上抛,寻找解决的viewGroup。

上述的工作流程模型并不是残缺的,还有其余的非凡状况没有思考。上面探讨几种非凡的状况:

事件序列被中断

咱们晓得,当一个view接管了down事件之后,该触控点接下来的事件都会被这个view生产。然而,viewGroup是能够在中途掐断事件流的,因为每一个须要分发给子view的事件都须要通过拦挡办法:onInterceptTouchEvent (当然,这里不探讨子view设置不拦挡标记的状况)。那么当viewGroup掐断事件流之后,事件的走向又是如何的呢?参看下图:(留神,这里不探讨多指操作的状况,仅探讨单指操作的moveup事件被viewGroup拦挡的状况

  1. 当viewGroup拦挡子view的move或up事件之后,会将以后事件改为cancel事件并发送给子view
  2. 如果以后事件序列还未完结,那些接下来的事件都会交给viewGroup的ouTouchEvent解决
  3. 此时不论是viewGroup还是view的onTouchEvent返回了false,那么将导致整个控件树的dispatchTouchEvent办法返回false

· 秉承着一个事件序列只能给一个view生产的准则,如果一个view耗费了down事件却在接下来的move或up事件返回了false,那么此事件不会给下层的viewGroup解决,而是间接返回false。

多点触控状况

下面探讨的所有状况,都是不蕴含多点触控状况的。多点触控的状况,在原有的事件散发流程上,新增了一些非凡状况。这里就不再画图,而是把一些非凡状况形容一下,读者理解一下就能够了。

默认状况下,viewGroup是反对多点触控的散发,但view是不反对多点触控的,须要本人去重写 dispatchTouchEvent 办法来反对多点触控。

多点触控的散发规定如下:

viewGroup在已有view承受了其余触点的down事件的状况下,另一个手指按下产生ACTION_POINTER_DOWN事件传递给viewGroup:

  1. viewGroup会依照ACTION_DOWN的形式去散发ACTION_POINTER_DOWN事件

· 如果子view生产该事件,那么和单点触控的流程统一

· 如果子view未生产该事件,那么会交给上一个最初接管down事件的view去解决

  1. viewGroup两个view接管了不同的down事件,那么拦挡其中一个view的事件序列,viewGroup不会生产拦挡的事件序列。换句话说,viewGroup和其中的view不能同时接管触摸事件。

Activity的事件散发

仔细的读者会发现,上述的工作流程并不波及Activity。咱们印象中的事件散发都是 Activity -> Window -> ViewGroup ,那么这是怎么回事?这所有,都是DecorView “惹的祸” 。

DecorView重写viewGroup的 dispatchTouchEvent 办法,当接管到触摸事件后,DecorView会首先把触摸对象传递给外部的callBack对象。没错,这个callBack对象就是Activity。退出Activity这个环节之后,散发的流程如下图所示:

整体上和后面的流程没有多大的不同,Activity继承了Window.CallBack接口,所以也有dispatchTouchEvent和onTouchEvent办法。对上图做个简略的剖析:

  1. activity接管到触摸事件之后,会间接把触摸事件分发给viewGroup
  2. 如果viewGroup的dispatchTouchEvent办法返回false,那么会调用Activity的onTouchEvent来处理事件
  3. 第1、2步的处理结果就是activity的dispatchTouchEvent办法的处理结果,并返回给下层

下面的流程不仅实用于Activity,同样实用于Dialog等应用DecorView和callback模式的控件零碎。

最初

到这里,事件散发的次要内容也就解说完了。联合前两篇文章,置信读者对于事件散发有更高的认知。

纸上得来终觉浅,绝知此事要躬行。学了常识之后最重要的就是实际。下一篇文章将简略剖析一下如何利用学习到的事件散发常识使用到理论开发中。