介绍
点击事件的事件分发就是对MotionEvent事件的分发过程,当一个MotionEvent产生了以后,系统需要把这个事件传递给一个具体的View,而这个传递的过程就是分发的过程。
涉及到的三个方法
- dispatchTouchEvent:用来进行事件的分发,如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和View的dispatchTouchEvent方法的影响,表示是否当消耗当前事件
- onInterceptTouchEvent:用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件;
- onTouchEvent:在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。
三个方法之间的关系
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">dispatchTouchEvent</span>(MotionEvent ev) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> consume = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
consume = child.dispatchTouchEvent(ev);
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> consume;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>
上面的伪代码很好的描述了三者之间的关系。如果当前View拦截事件,就交给自己的onTouchEvent去处理,否则就传给子View,直到事件被最终处理。
事件分发顺序
当一个点击事件产生后,它的传递过程如下:Activity -> Window -> View。如果View的onTouchEvent返回false,那么它的父容器onTouchEvent将会被调用,以此类推,最终将由Activity的onTouchEvent处理。
Activity对事件的分发过程
Activity -> Window -> DecorView。
Windows是一个抽象类,可以控制顶级View的外观和行为策略,PhoneWindow是这个类的唯一个实现。
DecorView就是当前界面的底层容器,即setContentView所设置的View是它的一个子View。
顶级View对点击事件的分发过程
ViewGroup -> dispatchTouchEvent -> onInterceptTouchEvent -> onTouch or onTouchEvent
顶级View一般都是一个ViewGroup。拦截事件之后,如果ViewGroup设置了mOnTouchListener,则Listener里的onTouch方法会屏蔽掉onTouchEvent。如果onTouchEvent设置了mOnClickListener,则Listener里的onClick会被调用。如果ViewGroup没有拦截则传给子View直到整个事件分发完成。
View对点击事件的处理过程
如果View设置了mOnTouchListener,则Listener里的onTouch方法会屏蔽掉onTouchEvent。如果onTouchEvent设置了mOnClickListener,则Listener里的onClick会被调用。
View没有onInterceptTouchEvent方法,一旦有点击事件传递给他,他就会处理。
转自: