`
hunankeda110
  • 浏览: 746582 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Android触摸事件分发机制

阅读更多

最近做了一个类似桌面应用图标可以点击,长按拖动效果,然后研究了下android触摸事件机制。

Android中的事件分为按键事件和触摸事件,这里对触摸事件进行阐述。Touch事件是由一个ACTION_DOWN,n个ACTION_MOVE,一个ACTION_UP组成onClick,onLongClick,onScroll等事件。Android中的控件都是继承View这个基类的,而控件分为两种:一种是继承View不能包含其他控件的控件;一种是继承ViewGroup可以包含其他控件的控件,暂且称为容器控件,比如ListView,GridView,LinearLayout等。

 

 

这里先对几个函数讲解下。

Ø  public boolean dispatchTouchEvent (MotionEventev)     这个方法分发TouchEvent

Ø  public booleanonInterceptTouchEvent(MotionEvent ev)  这个方法拦截TouchEvent

Ø  public boolean onTouchEvent(MotionEvent ev)          这个方法处理TouchEvent

其中view类中有dispatchTouchEvent和onTouchEvent两个方法,ViewGroup继承View,而且还新添了一个onInterceptTouchEvent方法。Activity中也无onInterceptTouchEvent方法,但有另外两种方法。我们可以发现上面3个方法都是返回boolean,那各代表什么意思呢?

 

     public boolean dispatchTouchEvent (MotionEvent ev) 

  Activity中解释:

Called to process touch screen events.You can override this to intercept all touch screen events before they aredispatched to the window. Be sure to call this implementation for touch screenevents that should be handled normally.

Parameters

ev

The touch screen event.

Returns

·        boolean Return true if this event was consumed.

它会被调用处理触摸屏事件,可以重写覆盖此方法来拦截所有触摸屏事件在这些事件分发到窗口之前。通常应该处理触摸屏事件,一定要调用这个实现。当返回值为true时,表示这个事件已经被消费了。例如在TextActivity中dispatchTouchEvent在ACTION_MOVE返回true,运行结果如下:

 

也就是它并没有把那ACTION_MOVE分发下去。

 

public boolean onInterceptTouchEvent (MotionEvent ev)

Implementthis method to intercept all touch screen motion events. This allows you towatch events as they are dispatched to your children, and take ownership of thecurrent gesture at any point.

Usingthis 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 thecorrect way. Events will be received in the following order:

1.    You will receive the down event here.

2.    The down event will be handled either by a child of this viewgroup, or given to your own onTouchEvent() method to handle; this means youshould implement onTouchEvent() to return true, so you will continue to see therest of the gesture (instead of looking for a parent view to handle it). Also,by returning true from onTouchEvent(), you will not receive any followingevents in onInterceptTouchEvent() and all touch processing must happen inonTouchEvent() like normal.

3.    For as long as you return false from this function, eachfollowing event (up to and including the final up) will be delivered first hereand then to the target's onTouchEvent().

4.    If you return true from here, you will not receive any followingevents: the target view will receive the same event but with the action ACTION_CANCEL, and all further events will be delivered to youronTouchEvent() method and no longer appear here.

Parameters

ev

The motion event being dispatched down the hierarchy.

Returns

·        Return true to steal motionevents from the children and have them dispatched to this ViewGroup throughonTouchEvent(). The current target will receive an ACTION_CANCEL event, and nofurther messages will be delivered here.

基本意思就是:

1. ACTION_DOWN首先会传递到onInterceptTouchEvent()方法

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

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

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

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

Android touch事件传递机制:

我们可以看看android源代码:

Activity.java中

 

暂且不管onUserInteraction方法因为它只是一个空方法如果你没实现的话。getWindow().superDispatchTouchEvent(ev)。其中getWindow()返回的是PhoneWindow。

PhoneWindow.java:

 

此函数调用super.dispatchTouchEvent(event),Activity的rootview是PhoneWindow.DecorView,它继承FrameLayout。通过super.dispatchTouchEvent把touch事件派发给各个Activity的是子view。同时我可以看到,如果子view拦截了事件,则不会执行onTouchEvent函数。

 

 

 

ViewGroup.javadispatchTouchEvent方法:

由于代码过长这里就不贴出来了,但也知道它返回的是

return target.dispatchTouchEvent(ev);

这里target指的是所分发的目标,可以是它本身,也可以是它的子View。

 

ViewGroup.java中的onInterceptTouchEvent方法:

 

 

默认情况下返回false。即不拦截touch事件。

 

 

View.java中的dispatchTouchEvent方法


 

这里我们很清楚可以知道如果if条件不成立则dispatchTouchEvent的返回值是onTouchEvent的返回值

View.java中的onTouchEvent方法

 

 

所以很容易得到触摸事件默认处理流程(以ACTION_DOWN事件为例):

 

当触摸事件ACTION_DOWN发生之后,先调用Activity中的dispatchTouchEvent函数进行处理,紧接着ACTION_DOWN事件传递给ViewGroup中的dispatchTouchEvent函数,接着viewGroup中的dispatchTouchEvent中的ACTION_DOWN事件传递到调用ViewGroup中的onInterceptTouchEvent函数,此函数负责拦截ACTION_DOWN事件。由于viewGroup下还包含子View,所以默认返回值为false,即不拦截此ACTION_DOWN事件。如果返回false,则ACTION_DOWN事件继续传递给其子view。由于子view不是viewGroup的控件,所以ACTION_DOWN事件接着传递到onTouchEvent进行处理事件。此时消息的传递基本上结束。从上可以分析,motionEvent事件的传递是采用隧道方式传递。隧道方式,即从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递。

接下来继续分析,事件的处理。刚才ACTION_DOWN事件传递到view的onTouchEvent函数中处理了,默认是返回true,接着view的dispatchTouchEvent返回true,再接着viewGroup的dispatchTouchEvent返回true,最后Activity的dispatchTouchEvent返回true。我们发现,motionEvent事件的处理采用冒泡方式。冒泡方式,从最内层子元素依次往外传递直到根元素或在中间某一元素中由于某一条件停止传递。

下图为程序调试结果:

ACTION_DOWN事件输出:

 

ACTION_MOVE事件输出:

 

现在我们来做一些改变,就接着以ACTION_DOWN为例

情况一:

我们在View中onTouchEvent中ACTION_DOWN返回false,输出结果如下:

 

可以发现ACTION_DOWN事件传递到上层的ViewGroup的onTouchEvent,同时返回true,说明事件被ViewGroup消费了。同时之后的touch事件(ACTION_MOVE等)不再传递给view,只传递到ViewGroup,由ViewGroup的onTouchEvent函数处理touch事件。同时onInterceptTouchEvent也不再调用。

情况二:

我们在View中onTouchEvent中ACTION_MOVE返回false,输出结果如下:

 

由于view未消费此ACTION_MOVE事件,按照原理来说应该是将事件处理冒泡到ViewGroup去处理,但结果却是Activity处理的。我们知道,触摸事件首先发生的就是ACTION_DOWN事件,我们在onInterceptTouchEvent所解释就可以发现ACTION_DOWN与ACTION_MOVE等事件有区别,ACTION_DOWN事件作为起始事件,它的重要性是要超过ACTION_MOVE和ACTION_UP的,如果发生了ACTION_MOVE或者ACTION_UP,那么一定曾经发生了ACTION_DOWN。也就是说ACTION_DOWN事件被view消费了,而ACTION_MOVE事件没被消费,传递到ViewGroup,由于之前ViewGroup没处理ACTION_DOWN事件,所以它也不处理ACTION_MOVE。但Activity却不一样,它可以接受所有事件。

情况三:

这次在ViewGroup中的onInterceptTouchEvent中ACTION_DOWN返回true

结果如下:

 

它直接把事件发送给ViewGroup的onTouchEvent处理,此后不再拦截事件直接到viewGroup中的onTouchEvent处理。

情况四:

在ViewGroup中的onInterceptTouchEvent中ACTION_MOVE返回true

结果如下:

 

ACTION_MOVE被ViewGroup拦截了,上次处理ACTION_DOWN的view则会收到ACTION_CANCEL事件,之后ViewGroup不再拦截后续事件,事件直接在ViewGroup中的onTouchEvent处理。

还有很多情况,这里不一一列出了。

 

在写这个demo一开始,我发现重写了onTouchEvent函数就无法获取onClick和onLongClick事件。接下来讨论当重写了onTouchEvent,android是如何区分onClick,onLongClick事件的。搞清楚此问题对于如何响应UI各种事件是很重要的,例如类似android桌面的应用程序图标,可以点击,然后长按拖动。

Android中onclick,onLongClick是都是由ACTION_DOWN,ACTION_UP组成。如果在同一个View中onTouchEvent、onclick、onLongClick都进行了重写。onTouchEvent最先捕获ACTION_DOWN、ACTION_UP等单元事件。接下来才可能发生onClick、onLongClick事件。一个onclick事件是由ACTION_DOWN和ACTION_UP组成的。一个onLongClick事件至少有一个ACTION_DOWN。那android具体是怎么实现的呢,可以看源代码:

View.java中:

 

上面我已经展示了onTouchEvent方法,但由于过长我折叠了一部分代码,现在展开

 

这个if条件内执行就是click事件处理及longClick事件处理。先看ACTION_DOWN事件

 

我们看到有个postDelayed方法,此方法意思为延时把线程插入到消息队列。即ACTION_DOWN后触发一个postDelayed方法。mPendingCheckForTap属于CheckForTap的实例。

 

在里面开启一个线程当为LONG_CLICKABLE,调用postCheckForLongClick方法。

 

再看mPendingCheckForLongPress这个线程。

 

 

当上面一系列条件全都符合的情况就调用performLongClick方法。

此方法就调用我们熟悉的onLongClick函数。

 

至此onLongClick事件已经分析完。再接着看ACTION_UP事件

 

 

直接关注performClick函数:

 

这里我们同样看到了我们熟悉的onClick方法。

所以android这种机制是保证了此onClick和onLongClick能与onTouchEvent并存。接下来考虑onclick与onLongClick是否并存,其实这个问题前面已经阐述了。只要此事件没被消费,它还会接着传递下去。从上面知道onLongClick是在单独的线程执行,发生在ACTION_UP之前。Onclick发生在ACTION_UP之后,也就是说,如果在onLongClick返回false,onclick就会发生,而onlongClick返回true,则代表此事件已经被消费。Onclick不再发生。

返回false


 

返回true


 

如果多次设置onclick事件,则最顶层的onclick覆盖掉底层onclick事件;多次设置onLongClick事件,则只执行底层view的onLongClick方法。当ACTION_DOWN调用之后返回false。

 

 

可以看到ACTION_DOWN被消费了,所以不会让上层处理了。

 

0
3
分享到:
评论
1 楼 fireAnt_T 2014-11-22  
讲的非常不错

相关推荐

    android中触摸事件的分发机制

    总结,Android触摸事件的分发机制是Android UI交互的基础,理解这一机制有助于开发者更有效地控制视图的行为,创建出更加丰富的用户界面。在实际开发中,可以通过灵活运用`onTouchEvent()`和`onInterceptTouchEvent...

    Android Touch事件分发机制代码一

    本文将深入解析Android的Touch事件分发机制,并通过一个名为"EventDispatchTest"的示例来说明。 1. **事件分发流程** Android中的触摸事件分发分为三个阶段:`dispatchTouchEvent()`, `onInterceptTouchEvent()`, ...

    [android][Android点击事件分发机制分析]

    事件分发机制是Android系统处理用户触摸事件的关键流程,理解这一机制对于优化UI交互和处理复杂视图层次结构至关重要。本文将深入探讨Android点击事件的分发过程,包括事件的产生、传递和处理。 首先,当用户在屏幕...

    Android事件分发机制

    总的来说,Android事件分发机制是构建用户界面交互的基础,理解这一机制对于优化用户体验和调试触摸问题至关重要。开发者应熟练掌握事件的分发流程,以及如何通过自定义View和ViewGroup来控制事件处理。通过实践和...

    android事件分发机制Demo

    在Android开发中,事件分发机制是理解和优化用户交互界面不可或缺的部分。事件主要指的是用户的触摸操作,例如点击、滑动等。当用户在屏幕上进行操作时,这些事件会经过一系列的处理和分发,最终到达相应的View进行...

    android事件分发机制

    为了深入理解Android触摸事件的处理流程,我们可以通过查看源码来进行更详细的解析。 1. **触摸事件的第一入口**:触摸事件首先被Activity的顶层 `ViewGroup` 接收,即 `dispatchTouchEvent` 方法。 ```java ...

    view的事件分发机制demo2

    在Android开发中,事件分发机制是理解和优化用户交互体验的关键环节。本文将深入探讨"view的事件分发机制demo2",并基于链接提供的详细讲解进行总结。 Android事件分发机制主要包括三个步骤: DispatchTouchEvent...

    Android事件分发机制总结Demo

    在Android开发中,事件分发机制是理解和优化用户交互界面不可或缺的部分。本文将深入探讨“Android事件分发机制总结Demo”,结合代码实例MyTouchEventDemo,帮助开发者清晰理解这一核心概念。 事件分发机制主要涉及...

    Android 事件分发机制视频讲解

    ### Android 事件分发机制详解 #### 一、引言 在Android开发中,事件处理是用户界面交互设计的核心组成部分。对于开发者来说,理解事件如何被分发到各个控件中至关重要,因为这直接影响到应用的用户体验。本文将从...

    Android事件分发机制小Demo

    在Android开发中,事件分发机制是至关重要的一个部分,它决定了用户与界面元素交互时的行为。本小Demo主要展示了Android事件分发的过程,并通过一系列的日志截图来帮助理解这一机制。下面,我们将深入探讨Android...

    android事件分发机制学习代码二

    在Android开发中,事件分发机制是至关重要的一个部分,它决定了用户与应用程序交互时,事件如何在View、 ViewGroup及Activity之间传递。本篇将基于"android事件分发机制学习代码二"的示例,深入解析Android事件分发...

    手写Android事件分发机制

    在Android开发中,事件分发机制是至关重要的一个部分,它决定了用户与界面交互时,事件如何在View、 ViewGroup及Activity之间传递。本教程将深入探讨如何手写Android事件分发机制,通过自定义代码来理解和模拟原生...

    Android事件分发机制(三)事件分发和消费

    在Android系统中,事件分发机制是用户与应用交互的核心环节,主要涉及到触摸事件的传递、处理和响应。本文将详细解析Android事件分发机制的第三部分:事件分发和消费,帮助开发者深入理解这一关键过程。 事件分发...

    事件分发机制

    在Android开发中,事件分发机制是理解和优化用户交互体验的关键环节。事件,通常指的是用户的触摸操作,如点击、滑动等,这些操作如何被准确地传递到相应的View上进行处理,就是事件分发机制的核心内容。下面我们将...

    Android View事件分发处理机制Demo

    Android的事件分发机制主要包括三个步骤:`dispatchTouchEvent()`, `onTouchEvent()`, 和 `onInterceptTouchEvent()`。这些方法在View、ViewGroup以及自定义View中起着关键作用。 1. **dispatchTouchEvent()**: ...

    Android事件分发机制演示demo

    在Android开发中,事件分发机制是一个至关重要的概念,它涉及到用户与应用程序界面的交互。事件分发机制决定了用户点击、触摸屏幕时,这些事件如何被Android系统正确地处理和传递到相应的视图组件。本Demo "Android...

    Android自定义View的事件分发机制(一)

    在博客文章“Android自定义View的事件分发机制(一)”中,作者jsonnan详细阐述了这些概念,并可能通过实例代码展示了如何在自定义View中实现自定义的事件分发逻辑。通过阅读这篇文章,开发者可以更深入地理解...

Global site tag (gtag.js) - Google Analytics