Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android MotionEvent事件響應機制

Android MotionEvent事件響應機制

編輯:關於Android編程

在android中,事件主要包括點擊、長按、拖曳、滑動等操作,這些構成了Android的事件響應,總體來說,所有的事件都由如下三個部分作為基礎構成:

按下(action_down),移動(action_move),抬起(action_up)。各種響應歸根結底都是基於View以及ViewGroup的,這兩者中響應的方法分別有:

View.java中:

publi boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)

ViewGroup.java中

public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
public boolean onInterceptTouchEvent(MotionEvent event)

在組件嵌套的情況下,對於事件的響應處理會從最頂層的組件不斷向子組件傳遞,一直到最後的View組件。

可以看到ViewGroup中比View中多出了一個onInterceptTouchEvent方法,這是因為ViewGroup組件可以存在子組件,因此需要通過Intercept判斷是否將該事件傳遞到子組件中。

函數的具體功能如下:

onTouchEvent是真正用來進行業務邏輯處理的地方,返回true表示已經將該事件消費,返回false表明事件繼續傳遞。

onInterceptTouchEvent是用來進行判斷是否需要對事件進行攔截從而阻止其繼續往子組件傳遞的,返回false表示無需攔截,則遞歸的調用子組件的dispatchTouchEvent方法;返回true表示需要攔截,則直接調用本組件的onTouchEvent方法進行處理。

以上兩個的功能相對好理解一些,最主要的是第三個,之前在網上看了很多,但是都沒有講的特別清楚的。大都說是用於事件分發的,返回true表示不繼續分發,返回false表示繼續分發。但是一直沒講明白這個跟采用onInterceptTouchEvent的區別在哪裡。。

直接點說,Android對於touch事件的處理是通過遞歸來進行的,而這種遞歸就體現在dispatchTouchEvent上。以上所寫的兩個函數就是在dispatchTouchEvent中被調用並且執行從而實現其分發的業務邏輯的。

在dispatchTouchEvent中有可能會調用三個方法:

1、本組件的onInterceptTouchEvent

2、子組件的dispatchTouchEvent

3、本組件的onTouchEvent

ViewGroup中dispatchTouchEvent()具體的執行邏輯:

1、首先執行本組件的onInterceptTouchEvent。如果返回false,表明無需攔截,則調用第二個方法,即子組件的dispatchTouchEvent方法;如果返回true,無需向子組件傳遞,則直接調用本組件的onTouchEvent方法

2、第一步中如果需要向子組件傳遞事件。如果遞歸調用子組件的dispatchTouchEvent返回false,則調用本組件的onTouchEvent方法;如果返回true,則無需調用本組件的onTouchEvent方法

3、根據前兩步的執行結果,將該dispatchTouchEvent的返回值返回給父組件的dispatchTouchEvent方法。


view中的dispatchTouchEvent會直接調用其自身的onTouchEvent。



一般沒有必要重寫dispatchTouchEvent方法,如果一定要重寫,請注意調用super.dispatchTouchEvent()方法,否則遞歸調用到此處即停止。

在不考慮dispatchTouchEvent的情況下,簡單的執行流程是這樣的:

最頂層的組件首先響應事件,然後不斷向子組件進行傳遞,調用子組件的onInterceptTouchEvent方法,一直到某個組件A的onInterceptTouchEvent方法返回true或者到達了view組件,然後調用該組件的onTouchEvent方法,之後不斷向父組件進行返回,調用父組件的onTouchEvent直到某個父組件的onTouchEvent方法返回true。

其實就是個首先從父組件不斷向下調用onInterceptTouchEvent,然後從子組件不斷向上調用onTouchEvent的過程。

還需要注意的:

1、假如這個過程中某個組件截獲並處理了ACTION_DOWN事件,則之後相應的ACTION_MOVE、ACTION_UP等其他事件將不再會被傳遞到他的子孫組件,而是傳遞到該組件後就執行返回的流程。

2、如果某個組件的onInterceptTouchEvent對ACTION_DOWN返回true,則之後的ACTION_MOVE,ACTION_DOWN等將不會再執行本onInterceptTouchEvent,而是直接傳遞給本組件的onTouchEvent,但是依然會經過其父組件的onInterceptTouchEvent。


過程中看到有篇文章裡對dispatchTouchEvent的代碼進行了注釋,可以更清楚的說明問題,具體如下,引用地址為:http://blog.csdn.net/hdxiaoyu2/article/details/25563453

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
        }


        // Check for interception.
        final boolean intercepted;//攔截的標記變量
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
            
                //調用自身的 onInterceptTochEvent,判斷是否需要攔截
                intercepted = onInterceptTochEvent(ev);
                ev.setAction(action); // restore action in case it was changed
            } else {
                intercepted = false;
            }
        } else {
            // There are no touch targets and this action is not an initial down
            // so this view group continues to intercept touches.
            intercepted = true;
        }


        
        //假如沒攔截
        if (!canceled && !intercepted) {
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

                if (newTouchTarget == null && childrenCount != 0) {
                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);
                    // Find a child that can receive the event.
                    // Scan children from front to back.
                    final View[] children = mChildren;

                    final boolean customOrder = isChildrenDrawingOrderEnabled();
                    //遍歷所有的子View,並且調用他們的事件分發方法dispatchTouchEvent()

                    for (int i = childrenCount - 1; i >= 0; i--) {                      final int childIndex = customOrder ?
                                getChildDrawingOrder(childrenCount, i) : i;
                        final View child = children[childIndex];
                        if (!canViewReceivePointerEvents(child)
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            continue;
                        }

                        newTouchTarget = getTouchTarget(child);
                        //newTouchTarget表示事件傳遞的View目標,當不為空的時候,直接跳出循環
                        if (newTouchTarget != null) {
                          
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                            break;
                        }

                        resetCancelNextUpFlag(child);
                        //遞歸調用子View分發事件方法,
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { 
                            
                            mLastTouchDownTime = ev.getDownTime();
                            mLastTouchDownIndex = childIndex;
                            mLastTouchDownX = ev.getX();
                            mLastTouchDownY = ev.getY();
                            //設置分發目標newTouchTarget為當前View
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            //標記子View的分發結果,為True的話,下面的代碼是不會調用當前View的onTouch方法的,也就是規則1生成的原因
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }
                    }
                }

            }
        }
        
        
        
        
        if (mFirstTouchTarget == null) {
            // No touch targets so treat this as an ordinary view.
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            // Dispatch to touch targets, excluding the new touch target if we already
            // dispatched to it.  Cancel touch targets if necessary.
            TouchTarget predecessor = null;
            TouchTarget target = mFirstTouchTarget;
            while (target != null) {
                final TouchTarget next = target.next;
                
                //如果剛才alreadyDispatchedToNewTouchTarget設為True的話,就不執行下面的dispatchTransformedTouchEvent
                //alreadyDispatchedToNewTouchTarget是由子View的onTouch返回值決定的,
                
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                } else {
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    
                    //執行自身的Touch事件,
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                        handled = true;
                    }
                    if (cancelChild) {
                        if (predecessor == null) {
                            mFirstTouchTarget = next;
                        } else {
                            predecessor.next = next;
                        }
                        target.recycle();
                        target = next;
                        continue;
                    }
                }
                predecessor = target;
                target = next;
            }
        }
    }

另外還看到兩篇將的不錯的,可以作為參考:

http://www.infoq.com/cn/articles/android-event-delivery-mechanism

http://blog.csdn.net/hdxiaoyu2/article/details/25563453


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved