View事件分发

转载自: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> &amp; 处理的整个过程</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> -&gt; <code>ViewGroup</code> -&gt; <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 -&gt; ViewGroup -&gt; 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">// -&gt;&gt;分析1</span>

            }

            <span class="hljs-comment">// -&gt;&gt;分析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,则方法结束。即 :该点击事件停止往下传递 &amp; 事件传递过程结束</span>
                <span class="hljs-comment">// 否则:继续往下调用Activity.onTouchEvent</span>

            }
            <span class="hljs-comment">// -&gt;&gt;分析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">// -&gt;&gt; 分析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">// -&gt;&gt; 分析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 &amp;&amp; event.getAction() == MotionEvent.ACTION_DOWN
            &amp;&amp; isOutOfBounds(context, event) &amp;&amp; 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() -&gt;&gt;分析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 &gt;= <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 &amp; 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 &amp;= ~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 &amp;= ~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 &amp; CANCEL_NEXT_UP_EVENT) != <span class="hljs-number">0</span>) {  
                ev.setAction(MotionEvent.ACTION_CANCEL);  
                mPrivateFlags &amp;= ~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() -&gt;&gt; onTouchEvent() -&gt;&gt; performClick() -&gt;&gt; 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">&lt;?xml version="1.0" encoding="utf-8"?&gt;</span>
<span class="hljs-tag">&lt;<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>&gt;</span>

    <span class="hljs-tag">&lt;<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> /&gt;</span>

    <span class="hljs-tag">&lt;<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> /&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-title">LinearLayout</span>&gt;</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> &amp;&amp; (mViewFlags &amp; ENABLED_MASK) == ENABLED &amp;&amp;  
                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 &amp; 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 &amp; 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 &amp; ENABLED_MASK) == DISABLED) {  

        <span class="hljs-keyword">return</span> (((viewFlags &amp; CLICKABLE) == CLICKABLE ||  
                (viewFlags &amp; 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 &amp; CLICKABLE) == CLICKABLE ||  
            (viewFlags &amp; 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 &amp; PREPRESSED) != <span class="hljs-number">0</span>;  

                            ...<span class="hljs-comment">// 经过种种判断,此处省略</span>

                            <span class="hljs-comment">// 执行performClick() -&gt;&gt;分析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 &amp;= ~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 &lt; <span class="hljs-number">0</span> - slop) || (x &gt;= getWidth() + slop) ||  
                                (y &lt; <span class="hljs-number">0</span> - slop) || (y &gt;= getHeight() + slop)) {  
                            <span class="hljs-comment">// Outside button  </span>
                            removeTapCallback();  
                            <span class="hljs-keyword">if</span> ((mPrivateFlags &amp; 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 &amp;= ~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() &amp; 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>左侧虚线:具备相关性 &amp; 逐层返回</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个方法的关系 &amp; 事件传递规则</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:最终返回通知 该事件是否被消费(接收 &amp; 处理)</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>常见的事件传递情况 &amp; 流程</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个方法的默认实现:调用下层的方法 &amp; 逐层返回</li>
<li>事件传递情况:(呈<code>U</code>型) <br>
<ol><li>从上往下调用dispatchTouchEvent() <br>


<blockquote>
  <p>Activity A -&gt;&gt; ViewGroup B -&gt;&gt; View C</p></blockquote></li>
  <li>从下往上调用onTouchEvent() <br>
  View C -&gt;&gt; ViewGroup B -&gt;&gt; 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>的行为是不一样的:不再传递 &amp; 接收该事件列的其他事件</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>事件 &amp; 传递给之前处理该事件的子<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>常见的事件传递情况 &amp; 流程已经讲解完毕。</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>&amp; 结束本次事件传递过程。 <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">// &amp;&amp;为短路与,即如果前面条件为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> &amp;&amp; (mViewFlags &amp; ENABLED_MASK) == ENABLED &amp;&amp;  
            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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值