您的位置:首页 > 移动开发 > Android开发

Android FrameWork——Touch事件派发过程详解

2015-03-19 11:51 645 查看
当发生touch事件时,系统会产生一个MotionEvent并且沿着View Tree开始传递。首先获取MotionEvent是View Tree的根节点,根节点通常是一个ViewGroup,ViewGroup将在onInterceptTouchEvent()中获取MotionEvent并决定是否继续向下传递。当在ViewGroup.onInterceptEvent()中返回true时,将截获MotionEvent,View Tree下面的View将无法获得MotionEvent,转而交给当前ViewGroup的onTouchEvent()方法。如果onTouchEvent中返回false,那么MotionEvent将沿着View Tree向上传给上一层。拦截的过程草图如下:



public boolean onInterceptTouchEvent(MotionEvent
ev)

Added in
API level 1

Implement this method to intercept all touch screen motion events. This allows you to watch events as they are dispatched to your children, and take ownership of the current gesture at any point.

Using this function takes some care, as it has a fairly complicated interaction with
View.onTouchEvent(MotionEvent)
,
and using it requires implementing that method as well as this one in the correct way. Events will be received in the following order:

You will receive the down event here.
The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking
for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.
For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().
If you return true from here, you will not receive any following events: the target view will receive the same event but with the action
ACTION_CANCEL
,
and all further events will be delivered to your onTouchEvent() method and no longer appear here.

Parameters
evThe motion event being dispatched down the hierarchy.
Returns

Return true to steal motion events from the children and have them dispatched to this ViewGroup through onTouchEvent(). The current target will receive an ACTION_CANCEL event, and no further messages will be delivered here.

老实说,这两个小东东实在是太麻烦了,很不好懂,我自己那api文档都头晕,在网上找到很多资料,才知道是怎么回事,这里总结一下,记住这个原则就会很清楚了:

1、onInterceptTouchEvent()是用于处理事件(类似于预处理,当然也可以不处理)并改变事件的传递方向,也就是决定是否允许Touch事件继续向下(子控件)传递,一但返回True(代表事件在当前的viewGroup中会被处理),则向下传递之路被截断(所有子控件将没有机会参与Touch事件),同时把事件传递给当前的控件的onTouchEvent()处理;返回false,则把事件交给子控件的onInterceptTouchEvent()

2、onTouchEvent()用于处理事件,返回值决定当前控件是否消费(consume)了这个事件,也就是说在当前控件在处理完Touch事件后,是否还允许Touch事件继续向上(父控件)传递,一但返回True,则父控件不用操心自己来处理Touch事件。返回true,则向上传递给父控件(注:可能你会觉得是否消费了有关系吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。)


概念介绍

1、onInterceptTouchEvent()是用于处理事件(重点onInterceptTouchEvent这个事件是从父控件开始往子控件传的,直到有拦截或者到没有这个事件的view,然后就往回从子到父控件,这次是onTouch的)(类似于预处理,当然也可以不处理)并改变事件的传递方向,也就是决定是否允许Touch事件继续向下(子控件)传递,一但返回True(代表事件在当前的viewGroup中会被处理),则向下传递之路被截断(所有子控件将没有机会参与Touch事件),同时把事件传递给当前的控件的onTouchEvent()处理;返回false,则把事件交给子控件的onInterceptTouchEvent()

2、onTouchEvent()用于处理事件(重点onTouch这个事件是从子控件回传到父控件的,一层层向下传),返回值决定当前控件是否消费(consume)了这个事件,也就是说在当前控件在处理完Touch事件后,是否还允许Touch事件继续向上(父控件)传递。返回false,则向上传递给父控件,详细一点就是这个touch事件就给了父控件,那么后面的up事件就是到这里touch触发,不会在传给它的子控件。如果父控件依然是false,那touch的处理就给到父控件的父控件,那么up的事件处理都在父控件的父控件,不会触发下面的。

返回true,如果是子控件返回true,那么它的touch事件都在这里处理,父控件是处理不了,因为它收不到子控件传给他的touch,被子控件给拦截了。(这里啰嗦了这么多就是为了加深记忆,这个两个事件理解起来都这么麻烦了,更何况去记,记我肯定是一下子就忘的了^0^)

(注:可能你会觉得是否消费了有关系吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。)


详细介绍

onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截,Android这么设计的想法也很好理解,由于ViewGroup会包含若干childView,因此需要能够统一监控各种touch事件的机会,因此纯粹的不能包含子view的控件是没有这个方法的,如LinearLayout就有,TextView就没有。

onInterceptTouchEvent()使用也很简单,如果在ViewGroup里覆写了该方法,那么就可以对各种touch事件加以拦截。但是如何拦截,是否所有的touch事件都需要拦截则是比较复杂的,touch事件在onInterceptTouchEvent()和onTouchEvent以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。并且,针对down事件处理的返回值直接影响到后续move和up事件的接收和传递。

关于返回值的问题,基本规则很清楚,如果return true,那么表示该方法消费了此次事件,如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。

onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截.

down事件首先会传递到onInterceptTouchEvent()方法

如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。

如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。

如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。

如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。

仅仅看这个官方文档解释,就能理解清楚这两个函数关系以及用途的绝对是富有经验的framework高手。

否则,一定需要一个案例来阐释。假设我们有这样一个layout,非常典型的

<ol class="linenums" style="list-style-position:outside; margin:0px; padding:0px 0px 0px 10px; border:none"><li class="L0" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="tag" style="color:maroon;font-weight:bold"><com.test.LayoutView1</span><span class="pln" style="color:#000000;"> </span><span class="atn" style="color:red;">xmlns:android</span><span class="pun" style="color:#000000;">=</span><span class="atv" style="color:blue;">"http://schemas.android.com/apk/res/android"</span><span class="pln" style="color:#000000;">  </span>
</li><li class="L1" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">    </span><span class="atn" style="color:red;">android:orientation</span><span class="pun" style="color:#000000;">=</span><span class="atv" style="color:blue;">"vertical"</span><span class="pln" style="color:#000000;"> </span><span class="atn" style="color:red;">android:layout_width</span><span class="pun" style="color:#000000;">=</span><span class="atv" style="color:blue;">"fill_parent"</span><span class="pln" style="color:#000000;">  </span>
</li><li class="L2" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">    </span><span class="atn" style="color:red;">android:layout_height</span><span class="pun" style="color:#000000;">=</span><span class="atv" style="color:blue;">"fill_parent"</span><span class="tag" style="color:maroon;font-weight:bold">></span><span class="pln" style="color:#000000;">  </span>
</li><li class="L3" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">    </span><span class="tag" style="color:maroon;font-weight:bold"><com.test.LayoutView2</span><span class="pln" style="color:#000000;">  </span>
</li><li class="L4" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">        </span><span class="atn" style="color:red;">android:orientation</span><span class="pun" style="color:#000000;">=</span><span class="atv" style="color:blue;">"vertical"</span><span class="pln" style="color:#000000;"> </span><span class="atn" style="color:red;">android:layout_width</span><span class="pun" style="color:#000000;">=</span><span class="atv" style="color:blue;">"fill_parent"</span><span class="pln" style="color:#000000;">  </span>
</li><li class="L5" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">        </span><span class="atn" style="color:red;">android:layout_height</span><span class="pun" style="color:#000000;">=</span><span class="atv" style="color:blue;">"fill_parent"</span><span class="pln" style="color:#000000;"> </span><span class="atn" style="color:red;">android:gravity</span><span class="pun" style="color:#000000;">=</span><span class="atv" style="color:blue;">"center"</span><span class="tag" style="color:maroon;font-weight:bold">></span><span class="pln" style="color:#000000;">  </span>
</li><li class="L6" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">        </span><span class="tag" style="color:maroon;font-weight:bold"><com.test.MyTextView</span><span class="pln" style="color:#000000;">  </span>
</li><li class="L7" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">            </span><span class="atn" style="color:red;">android:layout_width</span><span class="pun" style="color:#000000;">=</span><span class="atv" style="color:blue;">"wrap_content"</span><span class="pln" style="color:#000000;">   </span><span class="atn" style="color:red;">android:layout_height</span><span class="pun" style="color:#000000;">=</span><span class="atv" style="color:blue;">"wrap_content"</span><span class="pln" style="color:#000000;">  </span>
</li><li class="L8" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">      </span><span class="tag" style="color:maroon;font-weight:bold">/></span><span class="pln" style="color:#000000;">  </span>
</li><li class="L9" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">    </span><span class="tag" style="color:maroon;font-weight:bold"></com.test.LayoutView2></span><span class="pln" style="color:#000000;">  </span>
</li><li class="L0" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;"></span><span class="tag" style="color:maroon;font-weight:bold"></com.test.LayoutView1></span></li></ol>


用一个示例图来解释这个layout:



通常外围的layoutview1,layoutview2,只是布局的容器不需要响应触屏的点击事件,仅仅Mytextview需要相应点击。但这只是一般情况,一些特殊的布局可能外围容器也要响应,甚至不让里面的mytextview去响应。更有特殊的情况是,动态更换响应对象。

那么首先看一下默认的触屏事件的在两个函数之间的传递流程。如下图:



如果仅仅想让MyTextView来响应触屏事件,让MyTextView的OnTouchEvent返回true,那么事件流就变成如下图,可以看到layoutview1,layoutview2已经不能进入OnTouchEvent:



另外一种情况,就是外围容器想独自处理触屏事件,那么就应该在相应的onInterceptTouchEvent函数中返回true,表示要截获触屏事件,比如layoutview1作截获处理,处理流变成如下图:



以此类推,我们可以得到各种具体的情况,整个layout的view类层次中都有机会截获,而且能看出来外围的容器view具有优先截获权。

当我们去做一些相对来讲具有更复杂的触屏交互效果的应用时候,经常需要动态变更touch event的处理对象,比如launcher待机桌面和主菜单(见下图),从滑动屏幕开始到停止滑动过程当中,只有外围的容器view才可以处理touch event,否则就会误点击上面的应用图标或者widget.反之在静止不动的状态下则需要能够响应图标(子view)的touch事件。摘取framework中abslistview代码如下

<ol class="linenums" style="list-style-position:outside; margin:0px; padding:0px 0px 0px 10px; border:none"><li class="L0" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="kwd" style="color:#008b;font-weight:bold">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;font-weight:bold">boolean</span><span class="pln" style="color:#000000;"> onInterceptTouchEvent</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;font-weight:bold">MotionEvent</span><span class="pln" style="color:#000000;"> ev</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;"></span>
</li><li class="L1" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">        </span><span class="kwd" style="color:#008b;font-weight:bold">int</span><span class="pln" style="color:#000000;"> action </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> ev</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">getAction</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;"></span>
</li><li class="L2" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;"></span>
</li><li class="L3" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">        </span><span class="kwd" style="color:#008b;font-weight:bold">switch</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">action </span><span class="pun" style="color:#000000;">&</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;font-weight:bold">MotionEvent</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">ACTION_MASK</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;"></span>
</li><li class="L4" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">        </span><span class="kwd" style="color:#008b;font-weight:bold">case</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;font-weight:bold">MotionEvent</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">ACTION_DOWN</span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;"></span>
</li><li class="L5" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;"> </span>
</li><li class="L6" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">            </span><span class="kwd" style="color:#008b;font-weight:bold">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">touchMode </span><span class="pun" style="color:#000000;">==</span><span class="pln" style="color:#000000;"> TOUCH_MODE_FLING</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;"></span>
</li><li class="L7" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">                </span><span class="kwd" style="color:#008b;font-weight:bold">return</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;font-weight:bold">true</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">  </span><span class="com" style="color:gray;">//fling状态,截获touch,因为在滑动状态,不让子view处理</span><span class="pln" style="color:#000000;"></span>
</li><li class="L8" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">            </span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;"></span>
</li><li class="L9" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">            </span><span class="kwd" style="color:#008b;font-weight:bold">break</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"></span>
</li><li class="L0" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">        </span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;"></span>
</li><li class="L1" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;"></span>
</li><li class="L2" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">        </span><span class="kwd" style="color:#008b;font-weight:bold">case</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;font-weight:bold">MotionEvent</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">ACTION_MOVE</span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;"></span>
</li><li class="L3" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">            </span><span class="kwd" style="color:#008b;font-weight:bold">switch</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">mTouchMode</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;"></span>
</li><li class="L4" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">            </span><span class="kwd" style="color:#008b;font-weight:bold">case</span><span class="pln" style="color:#000000;"> TOUCH_MODE_DOWN</span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;"></span>
</li><li class="L5" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">                </span><span class="kwd" style="color:#008b;font-weight:bold">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;font-weight:bold">int</span><span class="pln" style="color:#000000;"> pointerIndex </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> ev</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">findPointerIndex</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">mActivePointerId</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;"></span>
</li><li class="L6" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">                </span><span class="kwd" style="color:#008b;font-weight:bold">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;font-weight:bold">int</span><span class="pln" style="color:#000000;"> y </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;font-weight:bold">int</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> ev</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">getY</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">pointerIndex</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;"></span>
</li><li class="L7" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">                </span><span class="kwd" style="color:#008b;font-weight:bold">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">startScrollIfNeeded</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">y </span><span class="pun" style="color:#000000;">-</span><span class="pln" style="color:#000000;"> mMotionY</span><span class="pun" style="color:#000000;">))</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;"></span>
</li><li class="L8" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">                    </span><span class="kwd" style="color:#008b;font-weight:bold">return</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;font-weight:bold">true</span><span class="pun" style="color:#000000;">;</span><span class="com" style="color:gray;">//开始滑动状态,截获touch事件,不让子view处理</span><span class="pln" style="color:#000000;"></span>
</li><li class="L9" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">                </span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;"></span>
</li><li class="L0" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">                </span><span class="kwd" style="color:#008b;font-weight:bold">break</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"></span>
</li><li class="L1" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">            </span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;"></span>
</li><li class="L2" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">            </span><span class="kwd" style="color:#008b;font-weight:bold">break</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"></span>
</li><li class="L3" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;">        </span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;"></span>
</li><li class="L4" style="list-style:decimal; margin:0px; padding:0px 0px 0px 10px; border:none; line-height:20px; color:rgb(153,153,153); background-color:rgb(250,250,250)"><span class="pln" style="color:#000000;"></span><span class="pun" style="color:#000000;">}</span></li></ol>






总结:

仅仅通过概览性的官方文档是很难理解onInterceptTouchEvent函数的用途的,只有通过演绎这个抽象的规则,配以图文才能获取这个重要的知识。很显然,默认是返回false,不做截获。返回true之后,事件流的后端控件就没有机会处理touch事件了,把默认的事件流中每个处理函数看作一个节点,这个节点只要返回true, 后续的事件就被截止了,这样想就很好理解。

onInterceptTouchEvent是在ViewGroup里面定义的。Android中的layout布局类一般都是继承此类的。onInterceptTouchEvent是用于拦截手势事件的,每个手势事件都会先调用onInterceptTouchEvent。

onInterceptTouchEvent()用于处理事件并改变事件的传递方向。返回值为false时事件会传递给子控件的onInterceptTouchEvent();返回值为true时事件会传递给当前控件的onTouchEvent(),而不在传递给子控件,这就是所谓的Intercept(截断)。

onTouchEvent() 用于处理事件,返回值决定当前控件是否消费(consume)了这个事件。可能你要问是否消费了又区别吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。

ViewGroup里的onInterceptTouchEvent默认值是false这样才能把事件传给View里的onTouchEvent.

ViewGroup里的onTouchEvent默认值是false。



View里的onTouchEvent返回默认值是true.这样才能执行多次touch事件。

关于down事件的处理:

onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截,Android这么设计的想法也很好理解,由于ViewGroup会包含若干childView,因此需要能够统一监控各种touch事件的机会,因此纯粹的不能包含子view的控件是没有这个方法的,如LinearLayout就有,TextView就没有。
onInterceptTouchEvent()使用也很简单,如果在ViewGroup里覆写了该方法,那么就可以对各种touch事件加以拦截。但是如何拦截,是否所有的touch事件都需要拦截则是比较复杂的,touch事件在onInterceptTouchEvent()和onTouchEvent以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。并且,针对down事件处理的返回值直接影响到后续move和up事件的接收和传递。

关于返回值的问题,基本规则很清楚,如果return true,那么表示该方法消费了此次事件,如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。

SDK给出的说明如下:

· You will receive the down event here.
· The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should
implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent()
and all touch processing must happen in onTouchEvent() like normal.
· For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and
then to the target's onTouchEvent().
· If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL,
and all further events will be delivered to your onTouchEvent() method and no longer appear here.

由于onInterceptTouchEvent()的机制比较复杂,上面的说明写的也比较复杂,总结一下,基本的规则是:

1. down事件首先会传递到onInterceptTouchEvent()方法

2. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。

3. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。

4. 如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。

5. 如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。

下面用一个简单的实验说明上述复杂的规则。视图自底向上共3层,其中LayoutView1和LayoutView2就是LinearLayout, MyTextView就是TextView:

对应的xml布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<com.touchstudy.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.touchstudy.LayoutView2
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<com.touchstudy.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv"
android:text="AB"
android:textSize="40sp"
android:textStyle="bold"
android:background="#FFFFFF"
android:textColor="#0000FF"/>
</com.touchstudy.LayoutView2>
</com.touchstudy.LayoutView1>

下面看具体情况:

1. onInterceptTouchEvent()处理down事件均返回false,onTouchEvent()处理事件均返回true

------------------------------------------------------------------------------------------------------------------------------

04-11 03:58:42.620: DEBUG/LayoutView1(614): onInterceptTouchEvent action:ACTION_DOWN

04-11 03:58:42.620: DEBUG/LayoutView2(614): onInterceptTouchEvent action:ACTION_DOWN

04-11 03:58:42.620: DEBUG/MyTextView(614): onTouchEvent action:ACTION_DOWN

04-11 03:58:42.800: DEBUG/LayoutView1(614): onInterceptTouchEvent action:ACTION_MOVE

04-11 03:58:42.800: DEBUG/LayoutView2(614): onInterceptTouchEvent action:ACTION_MOVE

04-11 03:58:42.800: DEBUG/MyTextView(614): onTouchEvent action:ACTION_MOVE

…… //省略过多的ACTION_MOVE

04-11 03:58:43.130: DEBUG/LayoutView1(614): onInterceptTouchEvent action:ACTION_UP

04-11 03:58:43.130: DEBUG/LayoutView2(614): onInterceptTouchEvent action:ACTION_UP

04-11 03:58:43.150: DEBUG/MyTextView(614): onTouchEvent action:ACTION_UP

------------------------------------------------------------------------------------------------------------------------------

这是最常见的情况,onInterceptTouchEvent并没有做任何改变事件传递时序的操作,效果上和没有覆写该方法是一样的。可以看到,各种事件的传递本身是自底向上的,次序是:LayoutView1->LayoutView2->MyTextView。注意,在onInterceptTouchEvent均返回false时,LayoutView1和LayoutView2的onTouchEvent并不会收到事件,而是最终传递给了MyTextView。

2. LayoutView1的onInterceptTouchEvent()处理down事件返回true,

MyTextView的onTouchEvent()处理事件返回true

------------------------------------------------------------------------------------------------------------------------------

04-11 03:09:27.589: DEBUG/LayoutView1(446): onInterceptTouchEvent action:ACTION_DOWN

04-11 03:09:27.589: DEBUG/LayoutView1(446): onTouchEvent action:ACTION_DOWN

04-11 03:09:27.629: DEBUG/LayoutView1(446): onTouchEvent action:ACTION_MOVE

04-11 03:09:27.689: DEBUG/LayoutView1(446): onTouchEvent action:ACTION_MOVE

…… //省略过多的ACTION_MOVE

04-11 03:09:27.959: DEBUG/LayoutView1(446): onTouchEvent action:ACTION_UP

------------------------------------------------------------------------------------------------------------------------------

从Log可以看到,由于LayoutView1在拦截第一次down事件时return true,所以后续的事件(包括第一次的down)将由LayoutView1本身处理,事件不再传递下去。

3. LayoutView1,LayoutView2的onInterceptTouchEvent()处理down事件返回false,

MyTextView的onTouchEvent()处理事件返回false

LayoutView2的onTouchEvent()处理事件返回true

----------------------------------------------------------------------------------------------------------------------------

04-11 09:50:21.147: DEBUG/LayoutView1(301): onInterceptTouchEvent action:ACTION_DOWN

04-11 09:50:21.147: DEBUG/LayoutView2(301): onInterceptTouchEvent action:ACTION_DOWN

04-11 09:50:21.147: DEBUG/MyTextView(301): onTouchEvent action:ACTION_DOWN

04-11 09:50:21.147: DEBUG/LayoutView2(301): onTouchEvent action:ACTION_DOWN

04-11 09:50:21.176: DEBUG/LayoutView1(301): onInterceptTouchEvent action:ACTION_MOVE

04-11 09:50:21.176: DEBUG/LayoutView2(301): onTouchEvent action:ACTION_MOVE

04-11 09:50:21.206: DEBUG/LayoutView1(301): onInterceptTouchEvent action:ACTION_MOVE

04-11 09:50:21.217: DEBUG/LayoutView2(301): onTouchEvent action:ACTION_MOVE

…… //省略过多的ACTION_MOVE

04-11 09:50:21.486: DEBUG/LayoutView1(301): onInterceptTouchEvent action:ACTION_UP

04-11 09:50:21.486: DEBUG/LayoutView2(301): onTouchEvent action:ACTION_UP

----------------------------------------------------------------------------------------------------------------------------

可以看到,由于MyTextView在onTouchEvent()中return false,down事件被传递给其父view,即LayoutView2的onTouchEvent()方法处理,由于在LayoutView2的onTouchEvent()中return true,所以down事件传递并没有上传到LayoutView1。注意,后续的move和up事件均被传递给LayoutView2的onTouchEvent()处理,而没有传递给MyTextView。

----------------------------------------------------------------------------------------------------------------

应大家的要求,我把源代码贴上,其实很简单,就是基础文件,主要是用来观察事件的传递。

主Activity: InterceptTouchStudyActivity.java:

public class InterceptTouchStudyActivity extends Activity {
static final String TAG = "ITSActivity";
TextView tv;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layers_touch_pass_test);
}
}

LayoutView1.java:

public class LayoutView1 extends LinearLayout {
private final String TAG = "LayoutView1";
public LayoutView1(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG,TAG);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"onInterceptTouchEvent action:ACTION_DOWN");
// return true;
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"onInterceptTouchEvent action:ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"onInterceptTouchEvent action:ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG,"onInterceptTouchEvent action:ACTION_CANCEL");
break;
}

return false;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"onTouchEvent action:ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"onTouchEvent action:ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"onTouchEvent action:ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG,"onTouchEvent action:ACTION_CANCEL");
break;
}

return true;
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
super.onLayout(changed, l, t, r, b);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}

LayoutView2.java:

public class LayoutView2 extends LinearLayout {
private final String TAG = "LayoutView2";

public LayoutView2(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG,TAG);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"onInterceptTouchEvent action:ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"onInterceptTouchEvent action:ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"onInterceptTouchEvent action:ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG,"onInterceptTouchEvent action:ACTION_CANCEL");
break;
}

return false;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"onTouchEvent action:ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"onTouchEvent action:ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"onTouchEvent action:ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG,"onTouchEvent action:ACTION_CANCEL");
break;
}

return true;
}
}

MyTextView.java:

public class MyTextView extends TextView {
private final String TAG = "MyTextView";

public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG,TAG);
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"onTouchEvent action:ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"onTouchEvent action:ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"onTouchEvent action:ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG,"onTouchEvent action:ACTION_CANCEL");
break;
}

return false;
}

public void onClick(View v) {
Log.d(TAG, "onClick");
}

public boolean onLongClick(View v) {
Log.d(TAG, "onLongClick");
return false;
}
}

总结:最难理解的在于down事件处理完成后,后续的move和up事件会被经过一定的步骤到消费down事件的函数中(onTouchEvent返回true)再次进行处理。但是up和down事件不会再向下传递,而是经过父组件的onInterceptTouchEvent函数后,直接进入处理down组件的onTouchEvent函数中处理,不会调用该组件(groupview)的onInterceptTouchEvent函数。也就是说哪个组件处理的down事件,后续的事件可定要到该处理事件的函数onTouchEvent中再次处理后续的move和up事件。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: