zoukankan      html  css  js  c++  java
  • 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 plain copy
     
     在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 plain copy
     
     在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 plain copy
     
     在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 plain copy
     
     在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

  • 相关阅读:
    【Codechef】Chef and Bike(二维多项式插值)
    USACO 完结的一些感想
    USACO 6.5 Checker Challenge
    USACO 6.5 The Clocks
    USACO 6.5 Betsy's Tour (插头dp)
    USACO 6.5 Closed Fences
    USACO 6.4 Electric Fences
    USACO 6.5 All Latin Squares
    USACO 6.4 The Primes
    USACO 6.4 Wisconsin Squares
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/5501000.html
Copyright © 2011-2022 走看看