事件分發 - 閱讀源碼、查漏補缺

View的事件處理:
dispatchTouchEvent -> onTouchEvent -> performClick -> 如果設置了setOnClickListener,那麼此時就會調用。

注意:如果View設置了setOnTouchListener,並且在回調方法onTouch裏面return true,那麼View的onTouchevent方法就不會執行,當然setOnClickListener的回調就更不會執行了。

//noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }


ViewGroup的事件處理:
dispatchTouchEvent -> dispatchTransformedTouchEvent -> super.dispatchTouchEvent or child.dispatchTouchEvent

注意:mFirstTouchTarget這個參數非常的重要,
1.在事件ACTION_DOWN、ACTION_POINTER_DOWN、ACTION_HOVER_MOVE中,遍歷子View,調用dispatchTransformedTouchEvent -> child.dispatchTouchEvent -> child.onTouchEvent,如果返回true,那麼就會將子View通過addTouchTarget加入到mFirstTouchTarget鏈表的頭部。

if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }

2.其他事件,就會去判斷mFirstTouchTarget是否爲null,如果爲null,則後續調用super.dispatchTouchEvent;如果不爲null,則依次遍歷mFirstTouchTarget,分別調用mFirstTouchTarget.child.dispatchTouchEvent。

// Dispatch to touch targets.
            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;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
//這裏的intercepted會受onInterceptTouchEvent方法的返回值影響,一旦intercepted爲true,將會把mFirstTouchTarget全部清除,並且對所有的target.child發送ACTION_CANCEL事件
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                        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;
                }
            }

這裏需要注意的是如果onInterceptTouchEvent返回爲true,那麼會給mFirstTouchTarget中所有的View分發ACTION_CANCEL事件,並且將mFirstTouchTarget中所有的Target全部移除掉,因此如果一個事件序列如果onInterceptTouchEvent返回爲true,那麼後續事件都不會分發給子View了,除非有一個新的事件ACTION_POINTER_DOWN的到來。

多點觸控:
注意:一個View裏面只能收到一次ACTION_DOWN事件,多次ACTION_POINTER_DOWN事件。但是如果是ViewGroup接受到ACTION_POINTER_DOWN事件,且第二個手指與第一個手指處於不同的兩個View,那麼會調用MotionEvent.split方法將ACTION_POINTER_DOWN變成ACTION_DOWN事件,傳遞給新的touchTarget.child中,因此在ViewGroup看來是接收到了一次ACTION_DOWN事件,但是不同的兩個View分別接收到了一次ACTION_DOWN事件,總計爲2次。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章