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

Android事件传递机制

2015-08-25 11:26 453 查看
      总结自其它人的博客,感觉比较清晰。

     参考:www.trinea.cn


Android Touch事件传递机制

介绍Android Touch事件的传递机制。
不少朋友私信问到这个问题,那就推荐一篇我看到的对传递机制介绍最清楚的国外文章吧。本文略作翻译。
 
1、基础知识
(1) 所有Touch事件都被封装成了MotionEvent对象,包括Touch的位置、时间、历史记录以及第几个手指(多指触摸)等。
 
(2) 事件类型分为ACTION_DOWN, ACTION_UP, ACTION_MOVE, ACTION_POINTER_DOWN, ACTION_POINTER_UP, ACTION_CANCEL,每个事件都是以ACTION_DOWN开始ACTION_UP结束。
 
(3) 对事件的处理包括三类,分别为传递——dispatchTouchEvent()函数、拦截——onInterceptTouchEvent()函数、消费——onTouchEvent()函数和OnTouchListener
 
2、传递流程
(1) 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。
 
(2) 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。
 
(3) 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。
 
(4) 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来。
 
(5) OnTouchListener优先于onTouchEvent()对事件进行消费。
 
上面的消费即表示相应函数返回值为true。
 
更多请直接阅读PDF英文原文:Mastering
the Android Touch System,

自己对源码的分析:触摸屏事件的传递机制源码分析

          ViewGroup中onInterceptTouchEvent();

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:
     实现这个方法可以打断所有的触摸屏事件,并且在触摸屏事件被分发到子视图时,允许你监管这些事件,随时获取事件的所有权。

使用这个方法,需要考虑到很多方面, 因为这个方法和onTouchEvent()方法的交互比较复杂,使用这个方法要求同样准确地实现onTouchEvent()方法。事件会以下面的顺序传递和接收:
1.  You will receive the down event here. 
     本方法收到按下事件DOWN

2.  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
       按下事件被当前视图的子视图处理,或被传递到当前viewgroup的onTouchEvent()方法中处理; 这就意味着,你需要实现onTouchEvent方法并返回true, 然后接着看剩下的其他手势(而不是寻找一个父视图处理), 其次, 通过在onTouchEvent中返回true后,你将不会再通过onInterceptTouchEvent()收到接下来其余的事件,并且所有的触屏事件处理都必须像通常情况一样在onTouchEvent中处理。
3.  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().      只要你在本方法中返回false,那么接下来的所有事件都会再次被传递进来,然后寻找目标视图的onTouchEvent()方法。
4.  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 
MotionEvent.ACTION_CANCEL
,
and all further events will be delivered to your onTouchEvent() method and no longer appear here.     如果你通过本方法返回true, 那么你将收不到接下来的所有事件,目标视图将受到相同的事件,不过, 同时伴随着事件MotionEvent.ACTION_CANCEL,并且所有接下来的事件都将被传递到当前视图的onTouchEvent()中,再也不会出现在本方法中。
参数:
ev
 - The motion event being dispatched down the hierarchy.返回: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.
          public boolean onInterceptTouchEvent(MotionEvent ev) {
                return false;
          }

View中onTouchEvent()
/**
     * Implement this method to handle touch screen motion events.
     * <p>
     * If this method is used to detect click actions, it is recommended that
     * the actions be performed by implementing and calling
     * {@link #performClick()}. This will ensure consistent system behavior,
     * including:
     * <ul>
     * <li>obeying click sound preferences
     * <li>dispatching OnClickListener calls
     * <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when
     * accessibility features are enabled
     * </ul>
     *
     * @param event The motion event.
     * @return True if the event was handled, false otherwise.
     */
 public boolean onTouchEvent(MotionEvent event);

 Android的Touch事件处理机制比较复杂,特别是在考虑了多点触摸以及事件拦截之后。

      Android的Touch事件处理分3个层面:Activity层,ViewGroup层,View层。

      首先说一下Touch事件处理的几条基本规则。

      1.如果在某个层级没有处理ACTION_DOWN事件,那么该层就再也收不到后续的Touch事件了直到下一次ACTION_DOWN事件。

         说明:a.某个层级没有处理某个事件指的是它以及它的子View都没有处理该事件。

                 b.这条规则不适用于Activity层(它是顶层),它们可以收到每一个Touch事件。

                 c.如果没有处理ACTION_MOVE这类事件,不会有任何影响。

      2.如果ACTION_DOWN事件发生在某个View的范围之内,则后续的ACTION_MOVE,ACTION_UP和ACTION_CANCEL等事件都将被发往该View,即使事件已经出界了。

      3.第一根按下的手指触发ACTION_DOWN事件,之后按下的手指触发ACTION_POINTER_DOWN事件,中间起来的手指触发ACTION_POINTER_UP事件,最后起来的手指触发ACTION_UP事件(即使它不是触发ACTION_DOWN事件的那根手指)。

      4.pointer id可以用于跟踪手指,从按下的那个时刻起pointer id生效,直至起来的那一刻失效,这之间维持不变。

      5.如果一个ACTION_DOWN事件被父View拦截了,则任何子View不会再收到任何Touch事件了(这符合第1点的要求)。

      6.如果一个非ACTION_DOWN事件被父View拦截了,则那些上次处理了ACTION_DOWN事件的子View会收到一个ACTION_CANCEL事件,之后不会再收到任何Touch事件了,即使父View不再拦截后续的Touch事件。

      7.如果父View决定处理Touch事件或者子View没有处理Touch事件,则父View按照普通View的处理方式处理Touch事件,否则它根本不处理Touch事件(它只负责分发)。

      8.如果父View在onInterceptTouchEvent中拦截了事件,则onInterceptTouchEvent中不会再收到Touch事件了,事件被直接交给它自己处理(按照普通View的处理方式)。

      下面分层讲述一些细节。

      1.Activity层:

publicboolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) { //在这里交给View层处理returntrue;
}
return onTouchEvent(ev); // 如果View层没有处理,则在这里处理
}


      2.View层:

publicboolean dispatchTouchEvent(MotionEvent event) {
// 省略了部分细节
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
returntrue;
}
if (onTouchEvent(event)) {
returntrue;
}
returnfalse;
}


      View的onTouch方法代码比较多,主要的逻辑分两步:先是将事件交给TouchDelegate处理(如果有的话),如果TouchDelegate没有处理再自行处理;自行处理主要负责View状态的变换(如按下状态),长按事件,点击事件的检测与触发等。

      3.ViewGroup层(比较复杂):

      ViewGroup层处理Touch事件的总体逻辑是:先检测是否需要拦截,没有拦截的话下发给子View处理,如果子View没有处理再自行处理,自行处理的逻辑与View一样。

      拦截的逻辑是,将从down到up之间的所有事件看作一组事件,如果从down就拦截了,则组内的后续其它事件完全交给自己处理,不需要再进入拦截逻辑了;如果是从中间拦截,则先给子View发送cancel事件,组内的后续其它事件完全交给自己处理,不需要再进入拦截逻辑了。

      分发的逻辑是,在ACTION_DOWN事件的时候,寻找子View进行处理,称为寻找Target;如果没有找到Target,则自行处理;如果找到Target,则交由Target处理。

      从代码上看,dispatchTouchEvent负责分发逻辑,onTouchEvent负责真正的处理逻辑,一般应该重载onTouchEvent,只有特殊情况下才需要重载dispatchTouchEvent。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: