Android的Touch系統簡介(一)

1、Android touch事件的相關概念java

用戶的Touch事件被包裝成MotionEvent
android

用戶當前的touch事件主要類型有:
數組

ACTION_DOWN: 表示用戶開始觸摸.app

 ACTION_MOVE: 表示用戶在移動(手指或者其餘)ide

 ACTION_UP:表示用戶擡起了手指 函數

ACTION_CANCEL:表示手勢被取消了,一些關於這個事件類型的討論見:http://stackoverflow.com/questions/11960861/what-causes-a-motionevent-action-cancel-in-android 源碼分析

ACTION_OUTSIDE: 表示用戶觸碰超出了正常的UI邊界.this

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

ACTION_POINTER_UP:一個非主要的手指擡起來了.net

touch事件的元數據包括:

touch的位置

手指的個數

touch事件的時間

一個touch手勢被定義爲以ACTION_DOWN開始和以 ACTION_UP結束。


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

相關文章
相關標籤/搜索