Android的Touch系统简介(一)

2014/11/11 16:57
阅读数 644

一、Android touch事件的相关概念

用户的Touch事件被包装成MotionEvent

用户当前的touch事件主要类型有:

ACTION_DOWN: 表示用户开始触摸.

 ACTION_MOVE: 表示用户在移动(手指或者其他)

 ACTION_UP:表示用户抬起了手指 

ACTION_CANCEL:表示手势被取消了,一些关于这个事件类型的讨论见:http://stackoverflow.com/questions/11960861/what-causes-a-motionevent-action-cancel-in-android 

ACTION_OUTSIDE: 表示用户触碰超出了正常的UI边界.

ACTION_POINTER_DOWN:有一个非主要的手指按下了.

ACTION_POINTER_UP:一个非主要的手指抬起来了

touch事件的元数据包括:

touch的位置

手指的个数

touch事件的时间

一个touch手势被定义为以ACTION_DOWN开始和以 ACTION_UP结束。


二、Touch事件的处理流程

当用户触摸屏幕时,触发Activity调用dispatchTouchEvent

事件对象会按自顶向下的顺序在View Tree中传递

     父View(ViewGroups)会调用dispatchTouchEvent将Event传递给子View    

    Event在任何时候都可能被拦截

事件流会顺着View链递归向下传递直到被消耗

若某个View想处理touch事件,必须先消耗ACTION_DOWN。考虑到效率,后续的事件将不会向下传递。

若某个事件未被消耗,最后会被Activity的onTouchEvent()消耗

若任何View或ViewGroup设置了OnTouchListener,touch事件将被拦截。


Activity.dispathcTouchEvent()的源码分析:

[java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. /** 

  2.     * Called to process touch screen events. You can override this to 

  3.     * intercept all touch screen events before they are dispatched to the 

  4.     * window. Be sure to call this implementation for touch screen events 

  5.     * that should be handled normally. 

  6.     * 

  7.     * @param ev The touch screen event. 

  8.     * 

  9.     * @return boolean Return true if this event was consumed. 

  10.     */  

  11.    public boolean dispatchTouchEvent(MotionEvent ev) {  

  12.        if (ev.getAction() == MotionEvent.ACTION_DOWN) {  

  13.            onUserInteraction();  

  14.        }  

  15.        if (getWindow().superDispatchTouchEvent(ev)) {  

  16.            return true;  

  17.        }  

  18.        return onTouchEvent(ev);  

  19.    }  


由代码可以看出,对于应用层,该函数在touch事件发生后首先被调用。onUserInteraction()是一个空函数,可被用户重载以进行相关处理。Event随后将被传递到关联到root view的window。若子view消耗了该Event,则返回true,否则Event最后被Activity的onTouchEvent()消耗。

ViewGroup.dispatchTouchEvent()的源码分析如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. public boolean dispatchTouchEvent(MotionEvent ev) {  

  2.         if (mInputEventConsistencyVerifier != null) {  

  3.             mInputEventConsistencyVerifier.onTouchEvent(ev, 1);  

  4.         }  

  5.         boolean handled = false;  

  6.         if (onFilterTouchEventForSecurity(ev)) {  

  7.             final int action = ev.getAction();  

  8.             final int actionMasked = action & MotionEvent.ACTION_MASK;  

  9.             // 处理初始的down事件  

  10.             if (actionMasked == MotionEvent.ACTION_DOWN) {  

  11.                 //当新开始一个touch事件时,抛弃先前的touch状态  

  12.                 //当app切换,发生ANR或一些其他的touch状态发生时,framework会丢弃或取消先前的touch状态  

  13.                 cancelAndClearTouchTargets(ev);  

  14.                 resetTouchState();  

  15.             }  

  16.             // 检查是否进行事件拦截  

  17.             final boolean intercepted;  

  18.             if (actionMasked == MotionEvent.ACTION_DOWN  

  19.                     || mFirstTouchTarget != null) {  

  20.                 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  

  21.                 if (!disallowIntercept) {  

  22.                     //回调onInterceptTouchEvent(),返回false表示不拦截touch,否则拦截touch事件。  

  23.                     intercepted = onInterceptTouchEvent(ev);  

  24.                     ev.setAction(action); // restore action in case it was changed  

  25.                 } else {  

  26.                     intercepted = false;  

  27.                 }  

  28.             } else {  

  29.                 //没有touch事件的传递对象,同时touch动作不是初始动作down,所以ViewGroup继续拦截事件  

  30.                 intercepted = true;  

  31.             }  

  32.             // 检查cancel事件  

  33.             final boolean canceled = resetCancelNextUpFlag(this)  

  34.                     || actionMasked == MotionEvent.ACTION_CANCEL;  

  35.             // 如果有第二个手指touch,更新touch目标列表。touch目标列表是一个View数组  

  36.             final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;  

  37.             TouchTarget newTouchTarget = null;  

  38.             boolean alreadyDispatchedToNewTouchTarget = false;  

  39.             if (!canceled && !intercepted) {  

  40.                 if (actionMasked == MotionEvent.ACTION_DOWN  

  41.                         || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)  

  42.                         || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {  

  43.                     final int actionIndex = ev.getActionIndex(); // always 0 for down  

  44.                     final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)  

  45.                             : TouchTarget.ALL_POINTER_IDS;  

  46.                     // Clean up earlier touch targets for this pointer id in case they  

  47.                     // have become out of sync.  

  48.                     removePointersFromTouchTargets(idBitsToAssign);  

  49.                     final int childrenCount = mChildrenCount;  

  50.                     if (newTouchTarget == null && childrenCount != 0) {  

  51.                         final float x = ev.getX(actionIndex);  

  52.                         final float y = ev.getY(actionIndex);  

  53.                         // 找到一个能接受Event的子View,再对子View的View树进行遍历  

  54.                         final View[] children = mChildren;  

  55.                         final boolean customOrder = isChildrenDrawingOrderEnabled();  

  56.                         //判断每个子View是否是TouchTarget,若是则添加到TouchTarget链表中  

  57.                         for (int i = childrenCount - 1; i >= 0; i--) {  

  58.                             final int childIndex = customOrder ?  

  59.                                     getChildDrawingOrder(childrenCount, i) : i;  

  60.                             final View child = children[childIndex];  

  61.                             if (!canViewReceivePointerEvents(child)  

  62.                                     || !isTransformedTouchPointInView(x, y, child, null)) {  

  63.                                 continue;  

  64.                             }  

  65.                             newTouchTarget = getTouchTarget(child);  

  66.                             if (newTouchTarget != null) {  

  67.                                 // 若子View处于touch目标中,同时已经接收了touch事件,则为器增加新的touch点  

  68.                                 newTouchTarget.pointerIdBits |= idBitsToAssign;  

  69.                                 break;  

  70.                             }  

  71.                             resetCancelNextUpFlag(child);  

  72.                             //把MotionEvent的点坐标转换到子View的坐标系中,为ViewGroup创建一个新TouchTarget,TouchTarget包含了子View  

  73.                             if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {  

  74.                                 // Child wants to receive touch within its bounds.  

  75.                                 mLastTouchDownTime = ev.getDownTime();  

  76.                                 mLastTouchDownIndex = childIndex;  

  77.                                 mLastTouchDownX = ev.getX();  

  78.                                 mLastTouchDownY = ev.getY();  

  79.                                 newTouchTarget = addTouchTarget(child, idBitsToAssign);  

  80.                                 alreadyDispatchedToNewTouchTarget = true;  

  81.                                 break;  

  82.                             }  

  83.                         }  

  84.                     }  

  85.                     if (newTouchTarget == null && mFirstTouchTarget != null) {  

  86.                         // 没有发现接收event的子View,把Touch点赋给最早添加到TouchTarget链中的对象  

  87.                         newTouchTarget = mFirstTouchTarget;  

  88.                         while (newTouchTarget.next != null) {  

  89.                             newTouchTarget = newTouchTarget.next;  

  90.                         }  

  91.                         newTouchTarget.pointerIdBits |= idBitsToAssign;  

  92.                     }  

  93.                 }  

  94.             }  

  95.             // 传递给touch目标  

  96.             if (mFirstTouchTarget == null) {  

  97.                 // 若没有Touch目标,则把自己当成一个View,调用  

  98.                 handled = dispatchTransformedTouchEvent(ev, canceled, null,  

  99.                         TouchTarget.ALL_POINTER_IDS);  

  100.             } else {  

  101.                 // Dispatch to touch targets, excluding the new touch target if we already  

  102.                 // dispatched to it. Cancel touch targets if necessary.  

  103.                 TouchTarget predecessor = null;  

  104.                 TouchTarget target = mFirstTouchTarget;  

  105.                 while (target != null) {  

  106.                     final TouchTarget next = target.next;  

  107.                     //若已被处理,则忽略。  

  108.                     if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {  

  109.                         handled = true;  

  110.                     } else {  

  111.                         final boolean cancelChild = resetCancelNextUpFlag(target.child)  

  112.                                 || intercepted;  

  113.                         //传递给子View处理  

  114.                         if (dispatchTransformedTouchEvent(ev, cancelChild,  

  115.                                 target.child, target.pointerIdBits)) {  

  116.                             handled = true;  

  117.                         }  

  118.                         if (cancelChild) {  

  119.                             if (predecessor == null) {  

  120.                                 mFirstTouchTarget = next;  

  121.                             } else {  

  122.                                 predecessor.next = next;  

  123.                             }  

  124.                             target.recycle();  

  125.                             target = next;  

  126.                             continue;  

  127.                         }  

  128.                     }  

  129.                     predecessor = target;  

  130.                     target = next;  

  131.                 }  

  132.             }  

  133.             // 若在触摸点发生了up或cancel,则更新TouchTarget链表  

  134.             if (canceled  

  135.                     || actionMasked == MotionEvent.ACTION_UP  

  136.                     || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {  

  137.                 resetTouchState();  

  138.             } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {  

  139.                 final int actionIndex = ev.getActionIndex();  

  140.                 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);  

  141.                 removePointersFromTouchTargets(idBitsToRemove);  

  142.             }  

  143.         }  

  144.         if (!handled && mInputEventConsistencyVerifier != null) {  

  145.             mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);  

  146.         }  

  147.         return handled;  

  148.     }  


ViewGroup中将TouchEvent传递给子View的函数为dispatchTransformedTouchEvent(),源代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. /** 

  2.     * Transforms a motion event into the coordinate space of a particular child view, 

  3.     * filters out irrelevant pointer ids, and overrides its action if necessary. 

  4.     * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. 

  5.     */  

  6.    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,  

  7.            View child, int desiredPointerIdBits) {  

  8.        final boolean handled;  

  9.        // Canceling motions is a special case. We don't need to perform any transformations  

  10.        // or filtering. The important part is the action, not the contents.  

  11.        // cancel动作是个特列,无需坐标转换或过滤。  

  12.        final int oldAction = event.getAction();  

  13.        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {  

  14.            event.setAction(MotionEvent.ACTION_CANCEL);  

  15.            if (child == null) {  

  16.                handled = super.dispatchTouchEvent(event);  

  17.            } else {  

  18.                handled = child.dispatchTouchEvent(event);  

  19.            }  

  20.            event.setAction(oldAction);  

  21.            return handled;  

  22.        }  

  23.        // 计算将被传递的点的数量。  

  24.        final int oldPointerIdBits = event.getPointerIdBits();  

  25.        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;  

  26.   

  27.        // Motion事件没有对应点,则丢弃这个Motion  

  28.        if (newPointerIdBits == 0) {  

  29.            return false;  

  30.        }  

  31.   

  32.        /*若点的数量一致则无需进行不相关的点坐标转换,调用子View的dispatchTouchEvent*/  

  33.        // If the number of pointers is the same and we don't need to perform any fancy  

  34.        // irreversible transformations, then we can reuse the motion event for this  

  35.        // dispatch as long as we are careful to revert any changes we make.  

  36.        // Otherwise we need to make a copy.  

  37.        /*该变量用于保存坐标转换后的MoetionEvent*/  

  38.        final MotionEvent transformedEvent;  

  39.        if (newPointerIdBits == oldPointerIdBits) {  

  40.            if (child == null || child.hasIdentityMatrix()) {  

  41.                if (child == null) {  

  42.                    handled = super.dispatchTouchEvent(event);  

  43.                } else {  

  44.                    final float offsetX = mScrollX - child.mLeft;  

  45.                    final float offsetY = mScrollY - child.mTop;      

  46.                    /*直接对MotionEvent进行坐标变换,将MotionEvent传递下去*/  

  47.                    event.offsetLocation(offsetX, offsetY);  

  48.                    handled = child.dispatchTouchEvent(event);  

  49.                    /*回复MotionEvent*/  

  50.                    event.offsetLocation(-offsetX, -offsetY);  

  51.                }  

  52.                return handled;  

  53.            }  

  54.            transformedEvent = MotionEvent.obtain(event);  

  55.        } else {  

  56.            transformedEvent = event.split(newPointerIdBits);  

  57.        }  

  58.        // Perform any necessary transformations and dispatch.  

  59.        if (child == null) {      

  60.            /*调用父类即View的dispatchTouchEvent方法,该方法会调用onTouchEvent*/  

  61.            handled = super.dispatchTouchEvent(transformedEvent);  

  62.        } else {  

  63.            final float offsetX = mScrollX - child.mLeft;  

  64.            final float offsetY = mScrollY - child.mTop;  

  65.            transformedEvent.offsetLocation(offsetX, offsetY);  

  66.            if (! child.hasIdentityMatrix()) {  

  67.                transformedEvent.transform(child.getInverseMatrix());  

  68.            }  

  69.            /*传递给子View处理*/  

  70.            handled = child.dispatchTouchEvent(transformedEvent);  

  71.        }  

  72.        // Done.  

  73.        transformedEvent.recycle();  

  74.        return handled;  

  75.    }  

View对象的dispatchTouchEvent代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. /** 

  2.    * Pass the touch screen motion event down to the target view, or this 

  3.    * view if it is the target. 

  4.    * 

  5.    * @param event The motion event to be dispatched. 

  6.    * @return True if the event was handled by the view, false otherwise. 

  7.    */  

  8.   public boolean dispatchTouchEvent(MotionEvent event) {  

  9.       if (mInputEventConsistencyVerifier != null) {  

  10.           mInputEventConsistencyVerifier.onTouchEvent(event, 0);  

  11.       }  

  12.       if (onFilterTouchEventForSecurity(event)) {  

  13.           //noinspection SimplifiableIfStatement  

  14.           ListenerInfo li = mListenerInfo;  

  15.           /*先调用listener接口*/  

  16.           if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED  

  17.                   && li.mOnTouchListener.onTouch(this, event)) {  

  18.               return true;  

  19.           }  

  20.          /*若MotionEvent未被消耗,则调用View的onTouchEvent * 

  21.           * ViewGroup中没有定义onTouchEvent,故做后调用View中的onTouchEvent*/  

  22.           if (onTouchEvent(event)) {  

  23.               return true;  

  24.           }  

  25.       }  

  26.       if (mInputEventConsistencyVerifier != null) {  

  27.           mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);  

  28.       }  

  29.       return false;  

  30.   }  


小结:

onInterceptTouchEvent:

onInterceptTouchEvent是在ViewGroup里面定义的,被ViewGroup.dispatchTouchEvent()调用,用于拦截所有的touch事件。默认返回false,表示不拦截touch事件,ViewGroup.dispatchTouchEvent()会调用子View的dispatchTouchEvent,将touch事件传递到子View中。若子View的dispatchTouchEvent 返回false,则ViewGroup的onTouchEvent会被调用;若子View的dispatchTouchEvent 返回true,表示消耗了手势事件,ViewGroup的onTouchEvent则不会被调用。若ViewGroup.onInterceptTouchEvent()返回true,表示Touch事件被拦截,ViewGroup. dispatchTransformedTouchEvent()函数将被调用,该函数会调用super.dispatchTouchEvent(event),即View的dispatchEvent(),该函数首先会调用View.OnTouchListener.onTouch().若listener未消耗Touch事件,则会调用View.onTouchEvent().  


onTouchEvent:

view中定义的方法onTouchEvent默认返回true,表示消耗了一个touch事件,ViewGroup中定义的onTouchEvent默认返回false,表示不处理Touch手势事件。手势事件类型包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL等事件。


本节及后续都是参考了一篇国外讲义,下载地址:http://download.csdn.net/detail/bigconvience/7376431


展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部