再回忆onInterceptTouchEvent和onTouchEvent

来源:互联网 发布:使命召唤9剧情知乎 编辑:程序博客网 时间:2024/06/07 14:20

原来转过一篇文章,是关于onInterceptTouchEvent和onTouchEvent的,时间久了都忘了差不多了,今天再重新回忆了一下,感觉理解又加深了一点。

测试的布局还是原来的,如下图所示:LayoutView1和LayoutView2都是LinearLayout, MyTextView是TextView.


情况一:
 LayoutView1和LayoutView2中的onInterceptTouchEvent的down事件均返回false,onTouchEvent
处理时间均返回true.MyTextView的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
    可以看出down事件没有经过LayoutView1和LayoutView2的onTouchEvent,而直接到了目标控件MyTextView.
  因为在LayoutView1和LayoutView2的onInterceptTouchEvent的down均返回false.即不拦截这个事件,
  所以会先向下传递,直到传递给了MyTextView, MyTextView中onTouchEvent返回了true,即消费了事件,所以后续
  的MOVE和UP事件不会传递给LayoutView2和LayoutView1的onTouchEvent。

情况二:当LayoutView1的onInterceptTouchEvent()处理down返回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本身处理,直接调用LayoutView1的onTouchEvent()函数,事件不再传递下去。
情况三:
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
----------------------------------------------------------------------------------------
首先是LayoutView1和LayoutView2不拦截down事件,所以down事件会传递给MyTextView.
由于MyTextView在onTouchEvent()中return false,down事件要向上继续传递给其父view,即LayoutView2
的onTouchEvent()方法处理,由于在LayoutView2的onTouchEvent()中return true,所以down事件传递并没有
上传到LayoutView1。注意,后续的move和up事件均被传递给LayoutView2的onTouchEvent()处理,而没有传递
给MyTextView。

情况四:
假设去掉LayoutView1和LayoutView2中的onInterceptTouchEvent:
06-12 17:23:08.547: D/MyTextView(6767): onTouchEvent action:ACTION_DOWN
06-12 17:23:08.547: D/LayoutView2(6767): onTouchEvent action:ACTION_DOWN
06-12 17:23:08.567: D/LayoutView2(6767): onTouchEvent action:ACTION_MOVE
06-12 17:23:08.577: D/LayoutView2(6767): onTouchEvent action:ACTION_MOVE
06-12 17:23:08.597: D/LayoutView2(6767): onTouchEvent action:ACTION_MOVE
06-12 17:23:08.607: D/LayoutView2(6767): onTouchEvent action:ACTION_UP
去掉onInterceptTouchEvent函数后,而ViewGroup中onInterceptTouchEvent函数默认返回false.所以down事件
会向下传递到MyTextView。MyTextView的onTouchEvent对down返回了false,
所以会向父控件传递到了LayoutView2,在LayoutView2中onTouchEvent对down返回了true,则down不再向上传递。所以
LayoutView2是最终处理down的控件,后续的move和up也只有这个控件来处理。

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

Android中跟Touch事件有关的事件有三个:
    public boolean dispatchTouchEvent(MotionEvent ev):传递Touch事件至target view(可以是自己)。
    public boolean onInterceptTouchEvent(MotionEvent ev):在ViewGroup中定义,用于拦截Touch事件的传递。
    public boolean onTouchEvent(MotionEvent event): Touch事件处理函数。


 *先说下事件传递的两种方式:
    隧道方式:从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递。 
    冒泡方式:从最内层子元素依次往外传递直到根元素或在中间某一元素中由于某一条件停止传递。
  Touch事件通过dispatchTouchEvent以隧道方式从上往下传递。如果在一个View中执行onTouchEvent时返回 true
的话,接下来的事件(ACTION_DOWN后的ACTION_UP,及可能在中间包含的若干个ACTION_MOVE,从 ACTION_DOWN至
ACTION_UP为一个连续事件,这是自己想的,不知道准确否)仍会传递到这个View的onTouchEvent,如果返回 false
的话,接下来的事件就不会再传递到这个View,而是执行其Parent View的onTouchEvent,每当一个View的
onTouchEvent事件返回false,接下来的事件(如果还有的话)就会止步于这个 View的Parent View,每次
上升一个层次,类似于冒泡方式。


  Touch事件传递过程中经过的元素都是一个View,但是事件处理的最外层的元素却不是View,除下跟Window有关
的事件,当一个 Touch事件发生的时候,会首先调用当前Activity的dispatchTouchEvent函数,然后才将事件传递
至下层的View元素。当 dispatchTouchEvent经过一个View往下传递的时候,如果这个View是一个ViewGroup,会调
用其 onInterceptTouchEvent函数,这个函数表示是否拦截Touch事件,如果这个函数返回true,表示这个ViewGroup
拦截了事 件的传递,Touch事件不会再往下传递给它的子View,而是由它处理,所以会调用它的onTouchEvent函数,
如果在传递的过程中没有 ViewGroup拦截事件,即经过的所有ViewGroup都返回false,那么事件最终会传递至最内层
的View,一般是一个Widget,当然也 可以是一ViewGroup(其内部不包含任何元素),如果最后事件传递到一View
(非ViewGroup),那么会首先调用这个View的 onTouchListener(如果设置了的话),如果onTouchListener返回
false则继续调用View的 onTouchEvent(默认返回true),如果最后事件传递到一ViewGroup(无子View),会调用
它的onTouchEvent函数,默 认返回false。


  如果调用一个View的onTouchEvent函数时返回true的话,那么接下来的Touch Event事件(ACTION_DOWN后的
ACTION_UP,及可能在中间包含的若干个ACTION_MOVE,从ACTION_DOWN至 ACTION_UP为一个连续事件,这是自己想的,
不知道准确否)仍会传递到这个View并调用它的onTouchEvent函数,在 onTouchEvent函数中可以根据条件返回不同的
值,如果某一次在此函数中返回了false那么接下来的Touch Event事件就不会再传递到这个View,而会在其
Parent View终止,调用其Parent View的onTouchEvent。如果所有的View都的onTouchEvent函数都返回false,
那么接下来的Touch Event事件会由Activity处理,即调用Activity的onTouchEvent函数。


  当调用ViewGroup的dispatchTouchEvent函数时,会首先调用onInterceptTouchEvent函数判断有没 有拦截
事件,如果没有拦截(返回false),则会依次调用这个ViewGroup的所有子View的dispatchTouchEvent函数。比
如一 个FrameLayout上层叠了三个ViewGroup,那么在这个FrameLayout的dispatchTouchEvent中会依次调用这三个
 ViewGroup的dispatchTouchEvent函数,而在这三个ViewGroup的dispatchTouchEvent中也会依次调用他 们的子
 View的dispatchTouchEvent函数,直到其中一个View的dispatchTouchEvent返回true,表示已经处理了 这个Touch
 事件,不需要再调用这个View的Slibling Views。比如调用这三个层叠的ViewGroup的dispatchTouchEvent函数时,
 如果第一个ViewGroup的 dispatchTouchEvent函数就返回了true(已经消耗掉了这个事件),那么其他两个
 ViewGroup的 dispatchTouchEvent就不会再被调用。可以自定义一个ViewGroup的子类并重载他的
 dispatchTouchEvent函数,使 其处理过Touch事件后仍返回false,那么就还会调用其他兄弟View的

 dispatchTouchEvent函数。


本帖记录onInterceptTouchEvent和onTouchEvent调用关系,即各种return true和return false的运行情况。

return true和return false,代表的是是否消费完该事件,也就是该事件是否会继续传递给下层或者上层组件继续处理。return true代表消费完不会继续传递,return false代表没有消费完将会继续传递。

如果没有onInterceptTouchEvent,只考虑onTouchEvent的话,比较容易分析和理解。假如有三层布局结构,linearLayout1,linearLayout2,textView,从前到后是包含的关系。那么下面分情况说明。
1.如果它们的onTouchEvent都返回false的话,DOWN事件会从外向内(textView位于最内层)依次传递,最终都没有消费完此事件,都只会进入onTouchEvent方法一次并且MotionEvent的action为MotionEvent.ACTION_DOWN,move和up等事件不会继续处理。
2.如果textView的onTouchEvent返回true,表示textView消费了此事件,则down事件不会传给父组件linearLayout2和linearLayout1了,并且后续move和up事件也只会传递给textview。
3.linearLayout2和linearLayout1的onTouchEvent返回true,则down事件都不会继续传给父容器而且本身继续处理move和up等事件。

下面加入onInterceptTouchEvent。
onInterceptTouchEvent只有ViewGroup才会有,用于在进入自身onTouchEvent或者子组件onTouchEvent之前处理事件。注意onTouch是从内向外传递,而onInterceptTouch却是由外向内传递的。来了一个DOWN事件,首先进入的必然是最外层的viewGroup的onInterceptTouchEvent方法,然后根据return的值进入自身或者子组件的onTouch事件,当然如果子组件也是viewgroup的话,在进入子组件的onTouch之前也会进入子组件的onInterceptTouchEvent方法。
下面也分几种情况介绍:
1.当onInterceptTouchEvent返回false时,表示没有消费完此事件,会继续传递个子组件的onTouch继续处理。注意这种情况不会就不会传递给这个ViewGroup自身的onTouch事件处理了。这和onTouch如果返回false,后续的move、up等事件都不会继续处理了可以做同样理解。
2.当onInterceptTouchEvent返回true时,表示消费完此事件,或者说将在此组件上消费该事件。这种情况该事件会传递给ViewGroup自身的onTouch事件去处理,而不会传递给子组件的onTouch方法了。
由此可以总结,onInterceptTouchEvent返回值只是决定了是要把事件传递给自身的onTouch事件还是传递给子组件的onTouch事件。返回false表示没有消费完将传递个子组件的onTouch方法,返回true表示自身消费此事件,将传递给自身的onTouch方法而不会传递给子组件的onTouch方法了。


原创粉丝点击