转载自:https://blog.csdn.net/carson_ho/article/details/54136311
<div id="article_content" class="article_content clearfix csdn-tracking-statistics" data-pid="blog" data-mod="popu_307" data-dsm="post">
<div class="markdown_views">
<h1 id="前言"><a name="t0"></a>前言</h1>
<ul>
<li><code>Android</code>事件分发机制是<code>Android</code>开发者必须了解的基础</li>
<li>网上有大量关于<code>Android</code>事件分发机制的文章,但存在一些问题:<strong>内容不全、思路不清晰、无源码分析、简单问题复杂化等等</strong></li>
<li>今天,我将全面总结<code>Android</code>的事件分发机制,我能保证这是<strong>市面上的最全面、最清晰、最易懂的</strong> <br>
<blockquote>
<ol>
<li>本文秉着“结论先行、详细分析在后”的原则,即先让大家感性认识,再通过理性分析从而理解问题; <br></li>
<li>所以,请各位读者先记住结论,再往下继续看分析;</li>
<li>文章较长,阅读需要较长时间,建议收藏等充足时间再进行阅读</li>
</ol></blockquote></li></ul> <br>
<hr>
<h1 id="目录"><a name="t1"></a>目录</h1>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-e7baca065f885271.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<hr>
<h1 id="1-基础认知"><a name="t2"></a>1. 基础认知</h1>
<h3 id="11-事件分发的对象是谁"><a name="t3"></a>1.1 事件分发的对象是谁?</h3>
<p><strong>答:点击事件(<code>Touch</code>事件)</strong></p>
<ul>
<li><p>定义 <br>
当用户触摸屏幕时(<code>View</code> 或 <code>ViewGroup</code>派生的控件),将产生点击事件(<code>Touch</code>事件)</p>
<blockquote>
<p><code>Touch</code>事件的相关细节(发生触摸的位置、时间等)被封装成<code>MotionEvent</code>对象</p>
</blockquote></li>
<li><p>事件类型(4种)</p></li>
</ul>
<table>
<thead>
<tr>
<th>事件类型</th>
<th align="center">具体动作</th>
</tr>
</thead>
<tbody><tr>
<td>MotionEvent.ACTION_DOWN</td>
<td align="center">按下View(所有事件的开始)</td>
</tr>
<tr>
<td>MotionEvent.ACTION_UP</td>
<td align="center">抬起View(与DOWN对应)</td>
</tr>
<tr>
<td>MotionEvent.ACTION_MOVE</td>
<td align="center">滑动View</td>
</tr>
<tr>
<td>MotionEvent.ACTION_CANCEL</td>
<td align="center">结束事件(非人为原因)</td>
</tr>
</tbody></table>
<ul>
<li>特别说明:事件列</li>
</ul>
<p>从手指接触屏幕 至 手指离开屏幕,这个过程产生的一系列事件</p>
<blockquote>
<p>注:一般情况下,事件列都是以<code>DOWN</code>事件开始、<code>UP</code>事件结束,中间有无数的MOVE事件,如下图: <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-79b1e86793514e99.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="事件列" title=""></p>
</blockquote>
<p>即当一个点击事件(<code>MotionEvent</code> )产生后,系统需把这个事件传递给一个具体的 <code>View</code> 去处理。 </p>
<h3 id="12-事件分发的本质"><a name="t4"></a>1.2 事件分发的本质</h3>
<p><strong>答:将点击事件(MotionEvent)传递到某个具体的<code>View</code> & 处理的整个过程</strong></p>
<blockquote>
<p>即 事件传递的过程 = 分发过程。</p>
</blockquote>
<h3 id="13-事件在哪些对象之间进行传递"><a name="t5"></a>1.3 事件在哪些对象之间进行传递?</h3>
<p><strong>答:Activity、ViewGroup、View</strong></p>
<ul>
<li><code>Android</code>的<code>UI</code>界面由<code>Activity</code>、<code>ViewGroup</code>、<code>View</code> 及其派生类组成 <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-ece40d4524784ffa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="UI界面" title=""></li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-02c588300f6ad741.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<h3 id="14-事件分发的顺序"><a name="t6"></a>1.4 事件分发的顺序</h3>
<p>即 事件传递的顺序:<code>Activity</code> -> <code>ViewGroup</code> -> <code>View</code></p>
<blockquote>
<p>即:1个点击事件发生后,事件先传到<code>Activity</code>、再传到<code>ViewGroup</code>、最终再传到 <code>View</code></p>
</blockquote>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-7fee82bba19a3821.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<h3 id="15-事件分发过程由哪些方法协作完成"><a name="t7"></a>1.5 事件分发过程由哪些方法协作完成?</h3>
<p><strong>答:dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()</strong></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-7c6642f518ffa3d2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<blockquote>
<p>下文会对这3个方法进行详细介绍</p>
</blockquote>
<h3 id="16-总结"><a name="t8"></a>1.6 总结</h3>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-d0a7e6f3c2bbefcc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<ul>
<li>至此,相信大家已经对 <code>Android</code>的事件分发有了感性的认知</li>
<li>下面,我将详细介绍<code>Android</code>事件分发机制</li>
</ul>
<hr>
<h1 id="2-事件分发机制-源码分析"><a name="t9"></a>2. 事件分发机制 源码分析</h1>
<ul>
<li>请谨记:<code>Android</code>事件分发流程 = <strong>Activity -> ViewGroup -> View</strong> <br>
<blockquote>
<p>即:1个点击事件发生后,事件先传到<code>Activity</code>、再传到<code>ViewGroup</code>、最终再传到 <code>View</code></p></blockquote></li>
</ul> <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-2064dcb69200fc6d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title="">
<ul>
<li>从上可知,要想充分理解Android分发机制,本质上是要理解: <br>
<ol><li><code>Activity</code>对点击事件的分发机制</li>
<li><code>ViewGroup</code>对点击事件的分发机制</li>
<li><code>View</code>对点击事件的分发机制</li></ol></li>
<li>下面,我将通过源码,全面解析 <strong>事件分发机制</strong> <br>
<blockquote>
即按顺序讲解:<code>Activity</code>事件分发机制、<code>ViewGroup</code>事件分发机制、<code>View</code>事件分发机制</blockquote></li>
</ul>
<h3 id="21-activity的事件分发机制"><a name="t10"></a>2.1 Activity的事件分发机制</h3>
当一个点击事件发生时,事件最先传到`Activity`的`dispatchTouchEvent()`进行事件分发
<h3 id="211-源码分析"><a name="t11"></a>2.1.1 源码分析</h3>
<pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-javadoc">/**
* 源码分析:Activity.dispatchTouchEvent()
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">dispatchTouchEvent</span>(MotionEvent ev) {
<span class="hljs-comment">// 一般事件列开始都是DOWN事件 = 按下事件,故此处基本是true</span>
<span class="hljs-keyword">if</span> (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
<span class="hljs-comment">// ->>分析1</span>
}
<span class="hljs-comment">// ->>分析2</span>
<span class="hljs-keyword">if</span> (getWindow().superDispatchTouchEvent(ev)) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
<span class="hljs-comment">// 若getWindow().superDispatchTouchEvent(ev)的返回true</span>
<span class="hljs-comment">// 则Activity.dispatchTouchEvent()就返回true,则方法结束。即 :该点击事件停止往下传递 & 事件传递过程结束</span>
<span class="hljs-comment">// 否则:继续往下调用Activity.onTouchEvent</span>
}
<span class="hljs-comment">// ->>分析4</span>
<span class="hljs-keyword">return</span> onTouchEvent(ev);
}
<span class="hljs-javadoc">/**
* 分析1:onUserInteraction()
* 作用:实现屏保功能
* 注:
* a. 该方法为空方法
* b. 当此activity在栈顶时,触屏点击按home,back,menu键等都会触发此方法
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onUserInteraction</span>() {
}
<span class="hljs-comment">// 回到最初的调用原处</span>
<span class="hljs-javadoc">/**
* 分析2:getWindow().superDispatchTouchEvent(ev)
* 说明:
* a. getWindow() = 获取Window类的对象
* b. Window类是抽象类,其唯一实现类 = PhoneWindow类;即此处的Window类对象 = PhoneWindow类对象
* c. Window类的superDispatchTouchEvent() = 1个抽象方法,由子类PhoneWindow类实现
*/</span>
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">superDispatchTouchEvent</span>(MotionEvent event) {
<span class="hljs-keyword">return</span> mDecor.superDispatchTouchEvent(event);
<span class="hljs-comment">// mDecor = 顶层View(DecorView)的实例对象</span>
<span class="hljs-comment">// ->> 分析3</span>
}
<span class="hljs-javadoc">/**
* 分析3:mDecor.superDispatchTouchEvent(event)
* 定义:属于顶层View(DecorView)
* 说明:
* a. DecorView类是PhoneWindow类的一个内部类
* b. DecorView继承自FrameLayout,是所有界面的父类
* c. FrameLayout是ViewGroup的子类,故DecorView的间接父类 = ViewGroup
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">superDispatchTouchEvent</span>(MotionEvent event) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.dispatchTouchEvent(event);
<span class="hljs-comment">// 调用父类的方法 = ViewGroup的dispatchTouchEvent()</span>
<span class="hljs-comment">// 即 将事件传递到ViewGroup去处理,详细请看ViewGroup的事件分发机制</span>
}
<span class="hljs-comment">// 回到最初的调用原处</span>
<span class="hljs-javadoc">/**
* 分析4:Activity.onTouchEvent()
* 定义:属于顶层View(DecorView)
* 说明:
* a. DecorView类是PhoneWindow类的一个内部类
* b. DecorView继承自FrameLayout,是所有界面的父类
* c. FrameLayout是ViewGroup的子类,故DecorView的间接父类 = ViewGroup
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onTouchEvent</span>(MotionEvent event) {
<span class="hljs-comment">// 当一个点击事件未被Activity下任何一个View接收 / 处理时</span>
<span class="hljs-comment">// 应用场景:处理发生在Window边界外的触摸事件</span>
<span class="hljs-comment">// ->> 分析5</span>
<span class="hljs-keyword">if</span> (mWindow.shouldCloseOnTouch(<span class="hljs-keyword">this</span>, event)) {
finish();
<span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
<span class="hljs-comment">// 即 只有在点击事件在Window边界外才会返回true,一般情况都返回false,分析完毕</span>
}
<span class="hljs-javadoc">/**
* 分析5:mWindow.shouldCloseOnTouch(this, event)
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">shouldCloseOnTouch</span>(Context context, MotionEvent event) {
<span class="hljs-comment">// 主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等</span>
<span class="hljs-keyword">if</span> (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != <span class="hljs-keyword">null</span>) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
<span class="hljs-comment">// 返回true:说明事件在边界外,即 消费事件</span>
<span class="hljs-comment">// 返回false:未消费(默认)</span>
}
<span class="hljs-comment">// 回到分析4调用原处</span></code><ul class="pre-numbering"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li><li style="color: rgb(153, 153, 153);">35</li><li style="color: rgb(153, 153, 153);">36</li><li style="color: rgb(153, 153, 153);">37</li><li style="color: rgb(153, 153, 153);">38</li><li style="color: rgb(153, 153, 153);">39</li><li style="color: rgb(153, 153, 153);">40</li><li style="color: rgb(153, 153, 153);">41</li><li style="color: rgb(153, 153, 153);">42</li><li style="color: rgb(153, 153, 153);">43</li><li style="color: rgb(153, 153, 153);">44</li><li style="color: rgb(153, 153, 153);">45</li><li style="color: rgb(153, 153, 153);">46</li><li style="color: rgb(153, 153, 153);">47</li><li style="color: rgb(153, 153, 153);">48</li><li style="color: rgb(153, 153, 153);">49</li><li style="color: rgb(153, 153, 153);">50</li><li style="color: rgb(153, 153, 153);">51</li><li style="color: rgb(153, 153, 153);">52</li><li style="color: rgb(153, 153, 153);">53</li><li style="color: rgb(153, 153, 153);">54</li><li style="color: rgb(153, 153, 153);">55</li><li style="color: rgb(153, 153, 153);">56</li><li style="color: rgb(153, 153, 153);">57</li><li style="color: rgb(153, 153, 153);">58</li><li style="color: rgb(153, 153, 153);">59</li><li style="color: rgb(153, 153, 153);">60</li><li style="color: rgb(153, 153, 153);">61</li><li style="color: rgb(153, 153, 153);">62</li><li style="color: rgb(153, 153, 153);">63</li><li style="color: rgb(153, 153, 153);">64</li><li style="color: rgb(153, 153, 153);">65</li><li style="color: rgb(153, 153, 153);">66</li><li style="color: rgb(153, 153, 153);">67</li><li style="color: rgb(153, 153, 153);">68</li><li style="color: rgb(153, 153, 153);">69</li><li style="color: rgb(153, 153, 153);">70</li><li style="color: rgb(153, 153, 153);">71</li><li style="color: rgb(153, 153, 153);">72</li><li style="color: rgb(153, 153, 153);">73</li><li style="color: rgb(153, 153, 153);">74</li><li style="color: rgb(153, 153, 153);">75</li><li style="color: rgb(153, 153, 153);">76</li><li style="color: rgb(153, 153, 153);">77</li><li style="color: rgb(153, 153, 153);">78</li><li style="color: rgb(153, 153, 153);">79</li><li style="color: rgb(153, 153, 153);">80</li><li style="color: rgb(153, 153, 153);">81</li><li style="color: rgb(153, 153, 153);">82</li><li style="color: rgb(153, 153, 153);">83</li><li style="color: rgb(153, 153, 153);">84</li><li style="color: rgb(153, 153, 153);">85</li><li style="color: rgb(153, 153, 153);">86</li><li style="color: rgb(153, 153, 153);">87</li><li style="color: rgb(153, 153, 153);">88</li><li style="color: rgb(153, 153, 153);">89</li><li style="color: rgb(153, 153, 153);">90</li><li style="color: rgb(153, 153, 153);">91</li><li style="color: rgb(153, 153, 153);">92</li><li style="color: rgb(153, 153, 153);">93</li><li style="color: rgb(153, 153, 153);">94</li><li style="color: rgb(153, 153, 153);">95</li><li style="color: rgb(153, 153, 153);">96</li><li style="color: rgb(153, 153, 153);">97</li><li style="color: rgb(153, 153, 153);">98</li><li style="color: rgb(153, 153, 153);">99</li><li style="color: rgb(153, 153, 153);">100</li><li style="color: rgb(153, 153, 153);">101</li><li style="color: rgb(153, 153, 153);">102</li><li style="color: rgb(153, 153, 153);">103</li><li style="color: rgb(153, 153, 153);">104</li><li style="color: rgb(153, 153, 153);">105</li><li style="color: rgb(153, 153, 153);">106</li><li style="color: rgb(153, 153, 153);">107</li></ul></pre>
<h3 id="212-总结"><a name="t12"></a>2.1.2 总结</h3>
<ul>
<li>当一个点击事件发生时,从<code>Activity</code>的事件分发开始(<code>Activity.dispatchTouchEvent()</code>)</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-63b84cd5c4d9dee9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<ul>
<li>方法总结</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-e186b0edcb590546.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p>那么,<code>ViewGroup</code>的<code>dispatchTouchEvent()</code>什么时候返回<code>true</code> / <code>false</code>?请继续往下看<strong>ViewGroup事件的分发机制</strong></p>
<hr>
<h1 id="22-viewgroup事件的分发机制"><a name="t13"></a>2.2 ViewGroup事件的分发机制</h1>
<p>从上面<code>Activity</code>事件分发机制可知,<code>ViewGroup</code>事件分发机制从<code>dispatchTouchEvent()</code>开始</p>
<h3 id="221-源码分析"><a name="t14"></a>2.2.1 源码分析</h3>
<blockquote>
<ol>
<li><code>Android 5.0</code>后,<code>ViewGroup.dispatchTouchEvent()</code>的源码发生了变化(更加复杂),但原理相同;</li>
<li>本文为了让读者容易理解,故采用<code>Android 5.0</code>前的版本</li>
</ol>
</blockquote>
<pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-javadoc">/**
* 源码分析:ViewGroup.dispatchTouchEvent()
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">dispatchTouchEvent</span>(MotionEvent ev) {
... <span class="hljs-comment">// 仅贴出关键代码</span>
<span class="hljs-comment">// 重点分析1:ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件</span>
<span class="hljs-keyword">if</span> (disallowIntercept || !onInterceptTouchEvent(ev)) {
<span class="hljs-comment">// 判断值1:disallowIntercept = 是否禁用事件拦截的功能(默认是false),可通过调用requestDisallowInterceptTouchEvent()修改</span>
<span class="hljs-comment">// 判断值2: !onInterceptTouchEvent(ev) = 对onInterceptTouchEvent()返回值取反</span>
<span class="hljs-comment">// a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),就会让第二个值为true,从而进入到条件判断的内部</span>
<span class="hljs-comment">// b. 若在onInterceptTouchEvent()中返回true(即拦截事件),就会让第二个值为false,从而跳出了这个条件判断</span>
<span class="hljs-comment">// c. 关于onInterceptTouchEvent() ->>分析1</span>
ev.setAction(MotionEvent.ACTION_DOWN);
<span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> scrolledXInt = (<span class="hljs-keyword">int</span>) scrolledXFloat;
<span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> scrolledYInt = (<span class="hljs-keyword">int</span>) scrolledYFloat;
<span class="hljs-keyword">final</span> View[] children = mChildren;
<span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> count = mChildrenCount;
<span class="hljs-comment">// 重点分析2</span>
<span class="hljs-comment">// 通过for循环,遍历了当前ViewGroup下的所有子View</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = count - <span class="hljs-number">1</span>; i >= <span class="hljs-number">0</span>; i--) {
<span class="hljs-keyword">final</span> View child = children[i];
<span class="hljs-keyword">if</span> ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != <span class="hljs-keyword">null</span>) {
child.getHitRect(frame);
<span class="hljs-comment">// 判断当前遍历的View是不是正在点击的View,从而找到当前被点击的View</span>
<span class="hljs-comment">// 若是,则进入条件判断内部</span>
<span class="hljs-keyword">if</span> (frame.contains(scrolledXInt, scrolledYInt)) {
<span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> xc = scrolledXFloat - child.mLeft;
<span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
<span class="hljs-comment">// 条件判断的内部调用了该View的dispatchTouchEvent()</span>
<span class="hljs-comment">// 即 实现了点击事件从ViewGroup到子View的传递(具体请看下面的View事件分发机制)</span>
<span class="hljs-keyword">if</span> (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
<span class="hljs-comment">// 调用子View的dispatchTouchEvent后是有返回值的</span>
<span class="hljs-comment">// 若该控件可点击,那么点击时,dispatchTouchEvent的返回值必定是true,因此会导致条件判断成立</span>
<span class="hljs-comment">// 于是给ViewGroup的dispatchTouchEvent()直接返回了true,即直接跳出</span>
<span class="hljs-comment">// 即把ViewGroup的点击事件拦截掉</span>
}
}
}
}
}
}
<span class="hljs-keyword">boolean</span> isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
<span class="hljs-keyword">if</span> (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
<span class="hljs-keyword">final</span> View target = mMotionTarget;
<span class="hljs-comment">// 重点分析3</span>
<span class="hljs-comment">// 若点击的是空白处(即无任何View接收事件) / 拦截事件(手动复写onInterceptTouchEvent(),从而让其返回true)</span>
<span class="hljs-keyword">if</span> (target == <span class="hljs-keyword">null</span>) {
ev.setLocation(xf, yf);
<span class="hljs-keyword">if</span> ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != <span class="hljs-number">0</span>) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.dispatchTouchEvent(ev);
<span class="hljs-comment">// 调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()</span>
<span class="hljs-comment">// 因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),即自己处理该事件,事件不会往下传递(具体请参考View事件的分发机制中的View.dispatchTouchEvent())</span>
<span class="hljs-comment">// 此处需与上面区别:子View的dispatchTouchEvent()</span>
}
...
}
<span class="hljs-javadoc">/**
* 分析1:ViewGroup.onInterceptTouchEvent()
* 作用:是否拦截事件
* 说明:
* a. 返回true = 拦截,即事件停止往下传递(需手动设置,即复写onInterceptTouchEvent(),从而让其返回true)
* b. 返回false = 不拦截(默认)
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onInterceptTouchEvent</span>(MotionEvent ev) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
}
<span class="hljs-comment">// 回到调用原处</span></code><ul class="pre-numbering"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li><li style="color: rgb(153, 153, 153);">35</li><li style="color: rgb(153, 153, 153);">36</li><li style="color: rgb(153, 153, 153);">37</li><li style="color: rgb(153, 153, 153);">38</li><li style="color: rgb(153, 153, 153);">39</li><li style="color: rgb(153, 153, 153);">40</li><li style="color: rgb(153, 153, 153);">41</li><li style="color: rgb(153, 153, 153);">42</li><li style="color: rgb(153, 153, 153);">43</li><li style="color: rgb(153, 153, 153);">44</li><li style="color: rgb(153, 153, 153);">45</li><li style="color: rgb(153, 153, 153);">46</li><li style="color: rgb(153, 153, 153);">47</li><li style="color: rgb(153, 153, 153);">48</li><li style="color: rgb(153, 153, 153);">49</li><li style="color: rgb(153, 153, 153);">50</li><li style="color: rgb(153, 153, 153);">51</li><li style="color: rgb(153, 153, 153);">52</li><li style="color: rgb(153, 153, 153);">53</li><li style="color: rgb(153, 153, 153);">54</li><li style="color: rgb(153, 153, 153);">55</li><li style="color: rgb(153, 153, 153);">56</li><li style="color: rgb(153, 153, 153);">57</li><li style="color: rgb(153, 153, 153);">58</li><li style="color: rgb(153, 153, 153);">59</li><li style="color: rgb(153, 153, 153);">60</li><li style="color: rgb(153, 153, 153);">61</li><li style="color: rgb(153, 153, 153);">62</li><li style="color: rgb(153, 153, 153);">63</li><li style="color: rgb(153, 153, 153);">64</li><li style="color: rgb(153, 153, 153);">65</li><li style="color: rgb(153, 153, 153);">66</li><li style="color: rgb(153, 153, 153);">67</li><li style="color: rgb(153, 153, 153);">68</li><li style="color: rgb(153, 153, 153);">69</li><li style="color: rgb(153, 153, 153);">70</li><li style="color: rgb(153, 153, 153);">71</li><li style="color: rgb(153, 153, 153);">72</li><li style="color: rgb(153, 153, 153);">73</li><li style="color: rgb(153, 153, 153);">74</li><li style="color: rgb(153, 153, 153);">75</li><li style="color: rgb(153, 153, 153);">76</li><li style="color: rgb(153, 153, 153);">77</li><li style="color: rgb(153, 153, 153);">78</li><li style="color: rgb(153, 153, 153);">79</li><li style="color: rgb(153, 153, 153);">80</li><li style="color: rgb(153, 153, 153);">81</li><li style="color: rgb(153, 153, 153);">82</li><li style="color: rgb(153, 153, 153);">83</li><li style="color: rgb(153, 153, 153);">84</li><li style="color: rgb(153, 153, 153);">85</li><li style="color: rgb(153, 153, 153);">86</li><li style="color: rgb(153, 153, 153);">87</li><li style="color: rgb(153, 153, 153);">88</li><li style="color: rgb(153, 153, 153);">89</li><li style="color: rgb(153, 153, 153);">90</li><li style="color: rgb(153, 153, 153);">91</li><li style="color: rgb(153, 153, 153);">92</li><li style="color: rgb(153, 153, 153);">93</li></ul></pre>
<h3 id="222-总结"><a name="t15"></a>2.2.2 总结</h3>
<ul>
<li>结论:<code>Android</code>事件分发总是先传递到<code>ViewGroup</code>、再传递到<code>View</code></li>
<li>过程:当点击了某个控件时</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-6ec2e864af7ffd37.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<ul>
<li>核心方法总结</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-ff627fea1a2244ad.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<h3 id="223-demo讲解"><a name="t16"></a>2.2.3 Demo讲解</h3>
<ul>
<li><p>布局如下 <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-b0bf3dd7ad41b335.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="布局层次" title=""></p></li>
<li><p>测试代码</p></li>
</ul>
<p>布局文件:<em>activity_main.xml</em></p>
<pre class="prettyprint" name="code"><code class="hljs xml has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span>
<span class="hljs-tag"><<span class="hljs-title">LinearLayout</span> <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span>
<span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/my_layout"</span>
<span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span>
<span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span>
<span class="hljs-attribute">xmlns:app</span>=<span class="hljs-value">"http://schemas.android.com/apk/res-auto"</span>
<span class="hljs-attribute">android:focusableInTouchMode</span>=<span class="hljs-value">"true"</span>
<span class="hljs-attribute">android:orientation</span>=<span class="hljs-value">"vertical"</span>></span>
<span class="hljs-tag"><<span class="hljs-title">Button
</span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/button1"</span>
<span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"wrap_content"</span>
<span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"wrap_content"</span>
<span class="hljs-attribute">android:text</span>=<span class="hljs-value">"按钮1"</span> /></span>
<span class="hljs-tag"><<span class="hljs-title">Button
</span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/button2"</span>
<span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"wrap_content"</span>
<span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"wrap_content"</span>
<span class="hljs-attribute">android:text</span>=<span class="hljs-value">"按钮2"</span> /></span>
<span class="hljs-tag"></<span class="hljs-title">LinearLayout</span>></span>
</code><ul class="pre-numbering"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li></ul></pre>
<p>核心代码:<em>MainActivity.java</em></p>
<pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-javadoc">/**
* ViewGroup布局(myLayout)中有2个子View = 2个按钮
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainActivity</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AppCompatActivity</span> {</span>
Button button1,button2;
ViewGroup myLayout;
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) {
<span class="hljs-keyword">super</span>.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1 = (Button)findViewById(R.id.button1);
button2 = (Button)findViewById(R.id.button2);
myLayout = (LinearLayout)findViewById(R.id.my_layout);
<span class="hljs-comment">// 1.为ViewGroup布局设置监听事件</span>
myLayout.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
Log.d(<span class="hljs-string">"TAG"</span>, <span class="hljs-string">"点击了ViewGroup"</span>);
}
});
<span class="hljs-comment">// 2. 为按钮1设置监听事件</span>
button1.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
Log.d(<span class="hljs-string">"TAG"</span>, <span class="hljs-string">"点击了button1"</span>);
}
});
<span class="hljs-comment">// 3. 为按钮2设置监听事件</span>
button2.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
Log.d(<span class="hljs-string">"TAG"</span>, <span class="hljs-string">"点击了button2"</span>);
}
});
}
}</code><ul class="pre-numbering"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li><li style="color: rgb(153, 153, 153);">35</li><li style="color: rgb(153, 153, 153);">36</li><li style="color: rgb(153, 153, 153);">37</li><li style="color: rgb(153, 153, 153);">38</li><li style="color: rgb(153, 153, 153);">39</li><li style="color: rgb(153, 153, 153);">40</li><li style="color: rgb(153, 153, 153);">41</li><li style="color: rgb(153, 153, 153);">42</li><li style="color: rgb(153, 153, 153);">43</li></ul></pre>
<ul>
<li>结果测试 <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-a9c45aa25d12b589.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></li>
</ul>
<p>从上面的测试结果发现:</p>
<ul>
<li>点击<code>Button</code>时,执行<code>Button.onClick()</code>,但<code>ViewGroupLayout</code>注册的<code>onTouch()</code>不会执行</li>
<li>只有点击空白区域时,才会执行<code>ViewGroupLayout</code>的<code>onTouch()</code></li>
<li>结论:<code>Button</code>的<code>onClick()</code>将事件消费掉了,因此事件不会再继续向下传递。</li>
</ul>
<hr>
<h1 id="23-view事件的分发机制"><a name="t17"></a>2.3 View事件的分发机制</h1>
<p>从上面<code>ViewGroup</code>事件分发机制知道,<code>View</code>事件分发机制从<code>dispatchTouchEvent()</code>开始</p>
<h3 id="231-源码分析"><a name="t18"></a>2.3.1 源码分析</h3>
<pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-javadoc">/**
* 源码分析:View.dispatchTouchEvent()
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">dispatchTouchEvent</span>(MotionEvent event) {
<span class="hljs-keyword">if</span> (mOnTouchListener != <span class="hljs-keyword">null</span> && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(<span class="hljs-keyword">this</span>, event)) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
}
<span class="hljs-keyword">return</span> onTouchEvent(event);
}
<span class="hljs-comment">// 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;否则执行onTouchEvent()</span>
<span class="hljs-comment">// 1. mOnTouchListener != null</span>
<span class="hljs-comment">// 2. (mViewFlags & ENABLED_MASK) == ENABLED</span>
<span class="hljs-comment">// 3. mOnTouchListener.onTouch(this, event)</span>
<span class="hljs-comment">// 下面对这3个条件逐个分析</span>
<span class="hljs-javadoc">/**
* 条件1:mOnTouchListener != null
* 说明:mOnTouchListener变量在View.setOnTouchListener()方法里赋值
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setOnTouchListener</span>(OnTouchListener l) {
mOnTouchListener = l;
<span class="hljs-comment">// 即只要我们给控件注册了Touch事件,mOnTouchListener就一定被赋值(不为空)</span>
}
<span class="hljs-javadoc">/**
* 条件2:(mViewFlags & ENABLED_MASK) == ENABLED
* 说明:
* a. 该条件是判断当前点击的控件是否enable
* b. 由于很多View默认enable,故该条件恒定为true
*/</span>
<span class="hljs-javadoc">/**
* 条件3:mOnTouchListener.onTouch(this, event)
* 说明:即 回调控件注册Touch事件时的onTouch();需手动复写设置,具体如下(以按钮Button为例)
*/</span>
button.setOnTouchListener(<span class="hljs-keyword">new</span> OnTouchListener() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onTouch</span>(View v, MotionEvent event) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
}
});
<span class="hljs-comment">// 若在onTouch()返回true,就会让上述三个条件全部成立,从而使得View.dispatchTouchEvent()直接返回true,事件分发结束</span>
<span class="hljs-comment">// 若在onTouch()返回false,就会使得上述三个条件不全部成立,从而使得View.dispatchTouchEvent()中跳出If,执行onTouchEvent(event)</span></code><ul class="pre-numbering"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li><li style="color: rgb(153, 153, 153);">35</li><li style="color: rgb(153, 153, 153);">36</li><li style="color: rgb(153, 153, 153);">37</li><li style="color: rgb(153, 153, 153);">38</li><li style="color: rgb(153, 153, 153);">39</li><li style="color: rgb(153, 153, 153);">40</li><li style="color: rgb(153, 153, 153);">41</li><li style="color: rgb(153, 153, 153);">42</li><li style="color: rgb(153, 153, 153);">43</li><li style="color: rgb(153, 153, 153);">44</li><li style="color: rgb(153, 153, 153);">45</li><li style="color: rgb(153, 153, 153);">46</li><li style="color: rgb(153, 153, 153);">47</li><li style="color: rgb(153, 153, 153);">48</li><li style="color: rgb(153, 153, 153);">49</li></ul></pre>
<p>接下来,我们继续看:<strong>onTouchEvent(event)</strong>的源码分析</p>
<blockquote>
<ol>
<li>详情请看注释</li>
<li><code>Android 5.0</code>后 <code>View.onTouchEvent()</code>源码发生了变化(更加复杂),但原理相同;</li>
<li>本文为了让读者更好理解,所以采用<code>Android 5.0</code>前的版本</li>
</ol>
</blockquote>
<pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-javadoc">/**
* 源码分析:View.onTouchEvent()
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onTouchEvent</span>(MotionEvent event) {
<span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> viewFlags = mViewFlags;
<span class="hljs-keyword">if</span> ((viewFlags & ENABLED_MASK) == DISABLED) {
<span class="hljs-keyword">return</span> (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
<span class="hljs-keyword">if</span> (mTouchDelegate != <span class="hljs-keyword">null</span>) {
<span class="hljs-keyword">if</span> (mTouchDelegate.onTouchEvent(event)) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
}
}
<span class="hljs-comment">// 若该控件可点击,则进入switch判断中</span>
<span class="hljs-keyword">if</span> (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
<span class="hljs-keyword">switch</span> (event.getAction()) {
<span class="hljs-comment">// a. 若当前的事件 = 抬起View(主要分析)</span>
<span class="hljs-keyword">case</span> MotionEvent.ACTION_UP:
<span class="hljs-keyword">boolean</span> prepressed = (mPrivateFlags & PREPRESSED) != <span class="hljs-number">0</span>;
...<span class="hljs-comment">// 经过种种判断,此处省略</span>
<span class="hljs-comment">// 执行performClick() ->>分析1</span>
performClick();
<span class="hljs-keyword">break</span>;
<span class="hljs-comment">// b. 若当前的事件 = 按下View</span>
<span class="hljs-keyword">case</span> MotionEvent.ACTION_DOWN:
<span class="hljs-keyword">if</span> (mPendingCheckForTap == <span class="hljs-keyword">null</span>) {
mPendingCheckForTap = <span class="hljs-keyword">new</span> CheckForTap();
}
mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress = <span class="hljs-keyword">false</span>;
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
<span class="hljs-keyword">break</span>;
<span class="hljs-comment">// c. 若当前的事件 = 结束事件(非人为原因)</span>
<span class="hljs-keyword">case</span> MotionEvent.ACTION_CANCEL:
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
removeTapCallback();
<span class="hljs-keyword">break</span>;
<span class="hljs-comment">// d. 若当前的事件 = 滑动View</span>
<span class="hljs-keyword">case</span> MotionEvent.ACTION_MOVE:
<span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> x = (<span class="hljs-keyword">int</span>) event.getX();
<span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> y = (<span class="hljs-keyword">int</span>) event.getY();
<span class="hljs-keyword">int</span> slop = mTouchSlop;
<span class="hljs-keyword">if</span> ((x < <span class="hljs-number">0</span> - slop) || (x >= getWidth() + slop) ||
(y < <span class="hljs-number">0</span> - slop) || (y >= getHeight() + slop)) {
<span class="hljs-comment">// Outside button </span>
removeTapCallback();
<span class="hljs-keyword">if</span> ((mPrivateFlags & PRESSED) != <span class="hljs-number">0</span>) {
<span class="hljs-comment">// Remove any future long press/tap checks </span>
removeLongPressCallback();
<span class="hljs-comment">// Need to switch from pressed to not pressed </span>
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
}
}
<span class="hljs-keyword">break</span>;
}
<span class="hljs-comment">// 若该控件可点击,就一定返回true</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
}
<span class="hljs-comment">// 若该控件不可点击,就一定返回false</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
}
<span class="hljs-javadoc">/**
* 分析1:performClick()
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">performClick</span>() {
<span class="hljs-keyword">if</span> (mOnClickListener != <span class="hljs-keyword">null</span>) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(<span class="hljs-keyword">this</span>);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
<span class="hljs-comment">// 只要我们通过setOnClickListener()为控件View注册1个点击事件</span>
<span class="hljs-comment">// 那么就会给mOnClickListener变量赋值(即不为空)</span>
<span class="hljs-comment">// 则会往下回调onClick() & performClick()返回true</span>
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
} </code><ul class="pre-numbering"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li><li style="color: rgb(153, 153, 153);">35</li><li style="color: rgb(153, 153, 153);">36</li><li style="color: rgb(153, 153, 153);">37</li><li style="color: rgb(153, 153, 153);">38</li><li style="color: rgb(153, 153, 153);">39</li><li style="color: rgb(153, 153, 153);">40</li><li style="color: rgb(153, 153, 153);">41</li><li style="color: rgb(153, 153, 153);">42</li><li style="color: rgb(153, 153, 153);">43</li><li style="color: rgb(153, 153, 153);">44</li><li style="color: rgb(153, 153, 153);">45</li><li style="color: rgb(153, 153, 153);">46</li><li style="color: rgb(153, 153, 153);">47</li><li style="color: rgb(153, 153, 153);">48</li><li style="color: rgb(153, 153, 153);">49</li><li style="color: rgb(153, 153, 153);">50</li><li style="color: rgb(153, 153, 153);">51</li><li style="color: rgb(153, 153, 153);">52</li><li style="color: rgb(153, 153, 153);">53</li><li style="color: rgb(153, 153, 153);">54</li><li style="color: rgb(153, 153, 153);">55</li><li style="color: rgb(153, 153, 153);">56</li><li style="color: rgb(153, 153, 153);">57</li><li style="color: rgb(153, 153, 153);">58</li><li style="color: rgb(153, 153, 153);">59</li><li style="color: rgb(153, 153, 153);">60</li><li style="color: rgb(153, 153, 153);">61</li><li style="color: rgb(153, 153, 153);">62</li><li style="color: rgb(153, 153, 153);">63</li><li style="color: rgb(153, 153, 153);">64</li><li style="color: rgb(153, 153, 153);">65</li><li style="color: rgb(153, 153, 153);">66</li><li style="color: rgb(153, 153, 153);">67</li><li style="color: rgb(153, 153, 153);">68</li><li style="color: rgb(153, 153, 153);">69</li><li style="color: rgb(153, 153, 153);">70</li><li style="color: rgb(153, 153, 153);">71</li><li style="color: rgb(153, 153, 153);">72</li><li style="color: rgb(153, 153, 153);">73</li><li style="color: rgb(153, 153, 153);">74</li><li style="color: rgb(153, 153, 153);">75</li><li style="color: rgb(153, 153, 153);">76</li><li style="color: rgb(153, 153, 153);">77</li><li style="color: rgb(153, 153, 153);">78</li><li style="color: rgb(153, 153, 153);">79</li><li style="color: rgb(153, 153, 153);">80</li><li style="color: rgb(153, 153, 153);">81</li><li style="color: rgb(153, 153, 153);">82</li><li style="color: rgb(153, 153, 153);">83</li><li style="color: rgb(153, 153, 153);">84</li><li style="color: rgb(153, 153, 153);">85</li><li style="color: rgb(153, 153, 153);">86</li><li style="color: rgb(153, 153, 153);">87</li><li style="color: rgb(153, 153, 153);">88</li><li style="color: rgb(153, 153, 153);">89</li><li style="color: rgb(153, 153, 153);">90</li><li style="color: rgb(153, 153, 153);">91</li><li style="color: rgb(153, 153, 153);">92</li></ul></pre>
<h3 id="232-总结"><a name="t19"></a>2.3.2 总结</h3>
<ul>
<li>每当控件被点击时:</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-76ce9e8299386729.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<blockquote>
<p>注:<code>onTouch()</code>的执行 先于 <code>onClick()</code></p>
</blockquote>
<ul>
<li>核心方法总结</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-762cf45f36858bbd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<h3 id="233-demo讲解"><a name="t20"></a>2.3.3 Demo讲解</h3>
<p>下面我将用<code>Demo</code>验证上述的结论</p>
<pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-javadoc">/**
* 结论验证1:在回调onTouch()里返回false
*/</span>
<span class="hljs-comment">// 1. 通过OnTouchListener()复写onTouch(),从而手动设置返回false</span>
button.setOnTouchListener(<span class="hljs-keyword">new</span> View.OnTouchListener() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onTouch</span>(View v, MotionEvent event) {
System.out.println(<span class="hljs-string">"执行了onTouch(), 动作是:"</span> + event.getAction());
<span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
}
});
<span class="hljs-comment">// 2. 通过 OnClickListener()为控件设置点击事件,为mOnClickListener变量赋值(即不为空),从而往下回调onClick()</span>
button.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
System.out.println(<span class="hljs-string">"执行了onClick()"</span>);
}
});
<span class="hljs-javadoc">/**
* 结论验证2:在回调onTouch()里返回true
*/</span>
<span class="hljs-comment">// 1. 通过OnTouchListener()复写onTouch(),从而手动设置返回true</span>
button.setOnTouchListener(<span class="hljs-keyword">new</span> View.OnTouchListener() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onTouch</span>(View v, MotionEvent event) {
System.out.println(<span class="hljs-string">"执行了onTouch(), 动作是:"</span> + event.getAction());
<span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
}
});
<span class="hljs-comment">// 2. 通过 OnClickListener()为控件设置点击事件,为mOnClickListener变量赋值(即不为空)</span>
<span class="hljs-comment">// 但由于dispatchTouchEvent()返回true,即事件不再向下传递,故不调用onClick())</span>
button.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
System.out.println(<span class="hljs-string">"执行了onClick()"</span>);
}
});
</code><ul class="pre-numbering"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li><li style="color: rgb(153, 153, 153);">35</li><li style="color: rgb(153, 153, 153);">36</li><li style="color: rgb(153, 153, 153);">37</li><li style="color: rgb(153, 153, 153);">38</li><li style="color: rgb(153, 153, 153);">39</li><li style="color: rgb(153, 153, 153);">40</li><li style="color: rgb(153, 153, 153);">41</li><li style="color: rgb(153, 153, 153);">42</li><li style="color: rgb(153, 153, 153);">43</li><li style="color: rgb(153, 153, 153);">44</li><li style="color: rgb(153, 153, 153);">45</li><li style="color: rgb(153, 153, 153);">46</li><li style="color: rgb(153, 153, 153);">47</li><li style="color: rgb(153, 153, 153);">48</li><li style="color: rgb(153, 153, 153);">49</li></ul></pre>
<ul>
<li>测试结果 <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-97959093583369a0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></li>
</ul>
<h1 id="24-总结"><a name="t21"></a>2.4 总结</h1>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-eeebede55f55b040.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<h3 id="若您已经看到此处那么恭喜你你已经能非常熟悉掌握android的事件分发机制了"><a name="t22"></a>若您已经看到此处,那么恭喜你,你已经能非常熟悉掌握Android的事件分发机制了</h3>
<blockquote>
<p>即:<code>Activity</code>、<code>ViewGroup</code>、<code>View</code> 的事件分发机制</p>
</blockquote>
<hr>
<h1 id="3-工作流程-总结"><a name="t23"></a>3. 工作流程 总结</h1>
<ul>
<li>在本节中,我将结合源码,梳理出1个事件分发的工作流程总结,具体如下:</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-aea821bbb613c195.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<blockquote>
<p>左侧虚线:具备相关性 & 逐层返回</p>
</blockquote>
<ul>
<li>以角色为核心的图解说明</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-bccafd3ff8a880ff.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<ul>
<li>以方法为核心的图解说明</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-9f340a39bdad520e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<hr>
<h1 id="4-核心方法总结"><a name="t24"></a>4. 核心方法总结</h1>
<ul>
<li><p>已知事件分发过程的核心方法为:<code>dispatchTouchEvent()</code>、<code>onInterceptTouchEvent()</code> 和 <code>onTouchEvent()</code> <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-faaf73d0f3eb870f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p></li>
<li><p>下面,我将结合总结的工作流程,再次详细讲解该3个方法</p></li>
</ul>
<h3 id="41-dispatchtouchevent"><a name="t25"></a>4.1 dispatchTouchEvent()</h3>
<ul>
<li>简介</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-4fbf11afa24b033b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-f8888a622c255648.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<ul>
<li>返回情况说明</li>
</ul>
<p><strong>情况1:默认</strong> <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-a4582905b3972904.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-fee3555bdc6f9524.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><strong>情况2:返回true</strong> <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-b00acc9f529f5393.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-6822b1af27852dbd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><strong>情况3:返回false</strong> <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-962fc440d2fffe0b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-0a9ae1bc05f432b9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<hr>
<h3 id="42-onintercepttouchevent"><a name="t26"></a>4.2 onInterceptTouchEvent()</h3>
<ul>
<li>简介</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-f4116863606e494e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<blockquote>
<p>注:<code>Activity</code>、<code>View</code>都无该方法</p>
</blockquote>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-28d0f5e7a1665148.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<ul>
<li>返回情况说明</li>
</ul>
<p><strong>情况1:true</strong></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-9a83aed00a8c0a54.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-6889eda6ebda8c40.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><strong>情况2:false(默认)</strong></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-ea06029e3176635f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-299cfcbe3a9c9fd5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<hr>
<h3 id="43-ontouchevent"><a name="t27"></a>4.3 onTouchEvent()</h3>
<ul>
<li>简介</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-b5f527027257b98e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-744f5b7e8d413562.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<ul>
<li>返回情况说明</li>
</ul>
<p><strong>情况1:返回true</strong></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-36af591c11a8e450.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-aef94c6c182353a9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><strong>情况2:返回false(default)</strong> <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-efd21a46a9af808e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-5da9fe2990f75d9c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<h3 id="44-三者关系"><a name="t28"></a>4.4 三者关系</h3>
<p>下面,我用一段伪代码来阐述上述3个方法的关系 & 事件传递规则</p>
<pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-javadoc">/**
* 点击事件产生后
*/</span>
<span class="hljs-comment">// 步骤1:调用dispatchTouchEvent()</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">dispatchTouchEvent</span>(MotionEvent ev) {
<span class="hljs-keyword">boolean</span> consume = <span class="hljs-keyword">false</span>; <span class="hljs-comment">//代表 是否会消费事件</span>
<span class="hljs-comment">// 步骤2:判断是否拦截事件</span>
<span class="hljs-keyword">if</span> (onInterceptTouchEvent(ev)) {
<span class="hljs-comment">// a. 若拦截,则将该事件交给当前View进行处理</span>
<span class="hljs-comment">// 即调用onTouchEvent ()方法去处理点击事件</span>
consume = onTouchEvent (ev) ;
} <span class="hljs-keyword">else</span> {
<span class="hljs-comment">// b. 若不拦截,则将该事件传递到下层</span>
<span class="hljs-comment">// 即 下层元素的dispatchTouchEvent()就会被调用,重复上述过程</span>
<span class="hljs-comment">// 直到点击事件被最终处理为止</span>
consume = child.dispatchTouchEvent (ev) ;
}
<span class="hljs-comment">// 步骤3:最终返回通知 该事件是否被消费(接收 & 处理)</span>
<span class="hljs-keyword">return</span> consume;
}</code><ul class="pre-numbering"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li></ul></pre>
<hr>
<h1 id="5-常见的事件分发场景"><a name="t29"></a>5. 常见的事件分发场景</h1>
<p>下面,我将通过实例说明<strong>常见的事件传递情况 & 流程</strong></p>
<h3 id="51-背景描述"><a name="t30"></a>5.1 背景描述</h3>
<ul>
<li>讨论的布局如下:</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-e0f526dd1b5731be.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<ul>
<li>情景 <br>
<ol><li>用户先触摸到屏幕上<code>View C</code>上的某个点(图中黄区) <br>
<blockquote>
<code>Action_DOWN</code>事件在此处产生</blockquote></li>
<li>用户移动手指</li>
<li>最后离开屏幕</li></ol></li>
</ul>
<h3 id="52-一般的事件传递情况"><a name="t31"></a>5.2 一般的事件传递情况</h3>
<p>一般的事件传递场景有:</p>
<ul>
<li>默认情况</li>
<li>处理事件</li>
<li>拦截<code>DOWN</code>事件</li>
<li>拦截后续事件(<code>MOVE</code>、<code>UP</code>)</li>
</ul>
<h3 id="场景1默认"><a name="t32"></a>场景1:默认</h3>
<ul>
<li>即不对控件里的方法(<code>dispatchTouchEvent()</code>、<code>onTouchEvent()</code>、<code>onInterceptTouchEvent()</code>)进行重写 或 更改返回值</li>
<li>那么调用的是这3个方法的默认实现:调用下层的方法 & 逐层返回</li>
<li>事件传递情况:(呈<code>U</code>型) <br>
<ol><li>从上往下调用dispatchTouchEvent() <br>
<blockquote>
<p>Activity A ->> ViewGroup B ->> View C</p></blockquote></li>
<li>从下往上调用onTouchEvent() <br>
View C ->> ViewGroup B ->> Activity A</li></ol></li>
</ul> <br>
<img src="http://upload-images.jianshu.io/upload_images/944365-161a6e6fc8723248.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title="">
<p>注:虽然<code>ViewGroup B</code>的<code>onInterceptTouchEvent</code>()对<code>DOWN</code>事件返回了<code>false</code>,但后续的事件<code>(MOVE、UP)</code>依然会传递给它的<code>onInterceptTouchEvent()</code> <br>
这一点与<code>onTouchEvent()</code>的行为是不一样的:不再传递 & 接收该事件列的其他事件</p>
<h3 id="场景2处理事件"><a name="t33"></a>场景2:处理事件</h3>
<p>设<code>View C</code>希望处理该点击事件,即:设置<code>View C</code>为可点击的<code>(Clickable)</code> 或 复写其<code>onTouchEvent()</code>返回<code>true</code></p>
<blockquote>
<p>最常见的:设置<code>Button</code>按钮来响应点击事件</p>
</blockquote>
<p>事件传递情况:(如下图)</p>
<ul>
<li><code>DOWN</code>事件被传递给C的<code>onTouchEvent</code>方法,该方法返回<code>true</code>,表示处理该事件</li>
<li>因为<code>View C</code>正在处理该事件,那么<code>DOWN</code>事件将不再往上传递给ViewGroup B 和 <code>Activity A</code>的<code>onTouchEvent()</code>;</li>
<li>该事件列的其他事件<code>(Move、Up)</code>也将传递给<code>View C</code>的<code>onTouchEvent()</code></li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-77e933eb44682777.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<blockquote>
<p>会逐层往<code>dispatchTouchEvent()</code> 返回,最终事件分发结束</p>
</blockquote>
<h3 id="场景3拦截down事件"><a name="t34"></a>场景3:拦截DOWN事件</h3>
<p>假设<code>ViewGroup B</code>希望处理该点击事件,即<code>ViewGroup B</code>复写了<code>onInterceptTouchEvent()</code>返回<code>true</code>、<code>onTouchEvent()</code>返回<code>true</code> <br>
事件传递情况:(如下图)</p>
<ul>
<li><code>DOWN</code>事件被传递给<code>ViewGroup B</code>的<code>onInterceptTouchEvent()</code>,该方法返回<code>true</code>,表示拦截该事件,即自己处理该事件(事件不再往下传递)</li>
<li><p>调用自身的<code>onTouchEvent()</code>处理事件(<code>DOWN</code>事件将不再往上传递给<code>Activity A</code>的<code>onTouchEvent()</code>)</p></li>
<li><p>该事件列的其他事件<code>(Move、Up)</code>将直接传递给<code>ViewGroup B</code>的<code>onTouchEvent()</code></p>
<blockquote>
<p>注:</p>
<ol><li>该事件列的其他事件<code>(Move、Up)</code>将不会再传递给<code>ViewGroup B</code>的<code>onInterceptTouchEvent</code>();因:该方法一旦返回一次<code>true</code>,就再也不会被调用</li>
<li>逐层往<code>dispatchTouchEvent()</code> 返回,最终事件分发结束</li></ol>
</blockquote></li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-a5e7cfed2cba02c3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<h3 id="场景4拦截down的后续事件"><a name="t35"></a>场景4:拦截DOWN的后续事件</h3>
<p><strong>结论</strong></p>
<ul>
<li>若 <code>ViewGroup</code> 拦截了一个半路的事件(如<code>MOVE</code>),该事件将会被系统变成一个<code>CANCEL</code>事件 & 传递给之前处理该事件的子<code>View</code>;</li>
<li>该事件不会再传递给<code>ViewGroup</code> 的<code>onTouchEvent()</code></li>
<li>只有再到来的事件才会传递到<code>ViewGroup</code>的<code>onTouchEvent()</code></li>
</ul>
<p><strong>场景描述</strong> <br>
<code>ViewGroup B</code> 无拦截<code>DOWN</code>事件(还是<code>View C</code>来处理<code>DOWN</code>事件),但它拦截了接下来的<code>MOVE</code>事件</p>
<blockquote>
<p>即 <code>DOWN</code>事件传递到<code>View C</code>的<code>onTouchEvent()</code>,返回了<code>true</code></p>
</blockquote>
<p><strong>实例讲解</strong></p>
<ul>
<li>在后续到来的MOVE事件,<code>ViewGroup B</code> 的<code>onInterceptTouchEvent()</code>返回<code>true</code>拦截该<code>MOVE</code>事件,但该事件并没有传递给<code>ViewGroup B</code> ;这个<code>MOVE</code>事件将会被系统变成一个<code>CANCEL</code>事件传递给<code>View C</code>的<code>onTouchEvent()</code></li>
<li><p>后续又来了一个<code>MOVE</code>事件,该<code>MOVE</code>事件才会直接传递给<code>ViewGroup B</code> 的<code>onTouchEvent()</code></p>
<blockquote>
<p>后续事件将直接传递给<code>ViewGroup B</code> 的<code>onTouchEvent()</code>处理,而不会再传递给<code>ViewGroup B</code> 的<code>onInterceptTouchEvent()</code>,因该方法一旦返回一次true,就再也不会被调用了。</p>
</blockquote></li>
<li><p><code>View C</code>再也不会收到该事件列产生的后续事件</p></li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/944365-1599f532038686cd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="示意图" title=""></p>
<p>至此,关于<code>Android</code>常见的事件传递情况 & 流程已经讲解完毕。</p>
<hr>
<h1 id="6-额外知识"><a name="t36"></a>6. 额外知识</h1>
<h3 id="61-touch事件的后续事件moveup层级传递"><a name="t37"></a>6.1 Touch事件的后续事件(MOVE、UP)层级传递</h3>
<p></p><ul> <br>
<li>若给控件注册了<code>Touch</code>事件,每次点击都会触发一系列<code>action</code>事件(ACTION_DOWN,ACTION_MOVE,ACTION_UP等)</li>
<li>当<code>dispatchTouchEvent()</code>事件分发时,只有前一个事件(如ACTION_DOWN)返回true,才会收到后一个事件(ACTION_MOVE和ACTION_UP) <br></li></ul><p></p>
<blockquote>
<p>即如果在执行ACTION_DOWN时返回false,后面一系列的ACTION_MOVE、ACTION_UP事件都不会执行
<br>
从上面对事件分发机制分析知:</p>
</blockquote>
<p></p><ul> <br>
<li>dispatchTouchEvent()、 onTouchEvent() 消费事件、终结事件传递(返回true) </li>
<li>而onInterceptTouchEvent 并不能消费事件,它相当于是一个分叉口起到分流导流的作用,对后续的ACTION_MOVE和ACTION_UP事件接收起到非常大的作用 <br></li></ul><p></p>
<blockquote>
<p>请记住:接收了ACTION_DOWN事件的函数不一定能收到后续事件(ACTION_MOVE、ACTION_UP)
<br>
<strong>这里给出ACTION_MOVE和ACTION_UP事件的传递结论</strong>:</p>
</blockquote>
<p></p><ul> <br>
<li>结论1 <br>
若对象(Activity、ViewGroup、View)的dispatchTouchEvent()分发事件后消费了事件(返回true),那么收到ACTION_DOWN的函数也能收到ACTION_MOVE和ACTION_UP <br></li></ul><p></p>
<blockquote>
<p>黑线:ACTION_DOWN事件传递方向 <br>
红线:ACTION_MOVE 、 ACTION_UP事件传递方向
<br>
<img src="http://upload-images.jianshu.io/upload_images/944365-93d0b1496e9e6ca4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="流程讲解" title=""></p>
</blockquote>
<p></p><ul> <br>
<li>结论2 <br>
若对象(Activity、ViewGroup、View)的onTouchEvent()处理了事件(返回true),那么ACTION_MOVE、ACTION_UP的事件从上往下传到该<code>View</code>后就不再往下传递,而是直接传给自己的<code>onTouchEvent()</code>& 结束本次事件传递过程。 <br></li></ul><p></p>
<blockquote>
<p>黑线:ACTION_DOWN事件传递方向 <br>
红线:ACTION_MOVE、ACTION_UP事件传递方向
<br>
<img src="http://upload-images.jianshu.io/upload_images/944365-9d639a0b9ebf7b4a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="流程讲解" title=""></p>
</blockquote>
<h3 id="62-ontouch和ontouchevent的区别"><a name="t38"></a>6.2 onTouch()和onTouchEvent()的区别</h3>
<ul>
<li>该2个方法都是在<code>View.dispatchTouchEvent()</code>中调用</li>
<li>但<code>onTouch()</code>优先于<code>onTouchEvent</code>执行;若手动复写在<code>onTouch()</code>中返回<code>true</code>(即 将事件消费掉),将不会再执行<code>onTouchEvent()</code></li>
</ul>
<blockquote>
<p>注:若1个控件不可点击(即非<code>enable</code>),那么给它注册<code>onTouch</code>事件将永远得不到执行,具体原因看如下代码</p>
</blockquote>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-comment">// &&为短路与,即如果前面条件为false,将不再往下执行</span>
<span class="hljs-comment">// 故:onTouch()能够得到执行需2个前提条件:</span>
<span class="hljs-comment">// 1. mOnTouchListener的值不能为空</span>
<span class="hljs-comment">// 2. 当前点击的控件必须是enable的</span>
mOnTouchListener != <span class="hljs-keyword">null</span> && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(<span class="hljs-keyword">this</span>, <span class="hljs-keyword">event</span>)
<span class="hljs-comment">// 对于该类控件,若需监听它的touch事件,就必须通过在该控件中重写onTouchEvent()来实现</span></code><ul class="pre-numbering"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li></ul></pre>
<hr>
<h1 id="7-总结"><a name="t39"></a>7. 总结</h1>
<ul>
<li>通过阅读本文,相信您已经可以全面了解<code>Android</code>事件分发机制</li>
<li><p>与<code>Android</code>事件分发最相关的知识:<strong>自定义View</strong>系列文章 <br>
<a href="http://blog.csdn.net/carson_ho/article/details/56009827" rel="nofollow" target="_blank">自定义View基础 - 最易懂的自定义View原理系列(1)</a> <br>
<a href="http://blog.csdn.net/carson_ho/article/details/56011064" rel="nofollow" target="_blank">自定义View Measure过程 - 最易懂的自定义View原理系列(2)</a> <br>
<a href="http://blog.csdn.net/carson_ho/article/details/56011112" rel="nofollow" target="_blank">自定义View Layout过程 - 最易懂的自定义View原理系列(3)</a> <br>
<a href="http://blog.csdn.net/carson_ho/article/details/56011153" rel="nofollow" target="_blank">自定义View Draw过程- 最易懂的自定义View原理系列(4)</a></p></li>
<li><p>接下来我将继续介绍与<code>Android</code>事件分发最相关的知识:<strong>自定义View</strong>,有兴趣可以继续关注<a href="http://blog.csdn.net/carson_ho" rel="nofollow" target="_blank">Carson_Ho的安卓开发笔记</a></p></li>
</ul>
<hr>
<h1 id="请帮顶-评论点赞因为你们的赞同鼓励是我写作的最大动力"><a name="t40"></a>请帮顶 / 评论点赞!因为你们的赞同/鼓励是我写作的最大动力!</h1> </div>
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/markdown_views-ea0013b516.css">
</div>