Android的Touch系统简介(一)

一、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

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

原文地址:https://www.cnblogs.com/Free-Thinker/p/4712642.html