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

  • 相关阅读:
    hdu4990矩阵快速幂
    预处理+状态压缩+剪枝——codefoece 1209E
    AC自动机处理多串匹配——cf1202E
    二维差分前缀和——cf1202D(好题)
    序列递推——cf1204E(好题)
    建模+线性dp——cf1201D
    暴力——cf1202C
    经典排序背包——cf1203F
    思维+贪心——cf1042D
    分块——cf1207F
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/5501000.html
Copyright © 2011-2022 走看看