深入探索Android 中view的touch事件传递

来源:互联网 发布:数据恢复精灵下载 编辑:程序博客网 时间:2024/04/19 10:59

每个View的子类都具有下面三个方法:

一、这个方法用来分发TouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {

                  //请求所有父控件及间接父控件不要拦截事件

             getParent().requestDisallowInterceptTouchEvent(true);

             return super.dispatchTouchEvent(ev);

    }

(1)如果返回true ,事件将会被终止。
(2)如果返回false,则交给上层view的onTouchEvent处理。

(3)返回super.dispatchTouchEvent(ev),才表明向下传递

二、这个方法用来拦截TouchEvent

         @Override

         publicboolean onInterceptTouchEvent(MotionEvent arg0) {

                  returnfalse;//false不拦截内层事件传递,true是拦截

         }
1、返回 true,则表示将事件进行拦截,并将拦截到的事件交由他的 onTouchEvent 进行处理;

2、返回结果是false;则表示不对事件进行拦截,事件将传递给上层View的dispatchTouchEvent。

返回super.onInterceptTouchEvent(ev),事件默认不会被拦截,但内部会进行判断。
3)这个方法用来处理触摸事件(TouchEvent)

         @Override

public booleanonTouchEvent(MotionEvent arg0) {

                  returntrue;//

         }

如果onTouchEvent返回true,说明事件被该view处理了。不在向下传递(即不向上层view传递),如果该方法中什么都不操作,那么触摸后就真的什么都不做。

如果onTouchEvent返回false,事件将会继续向下传递,直到被处理。

如果返回super.onTouchEvent(ev)。默认继续向下传递

执行顺序:dispatchTouchEvent->onInterceptTouchEvent->onTouchEvent

事件捕获机制:

1、当有监听事件时,首先由Activity的捕获到,进入事件分发处理流程。

2、如果事件分发返回系统默认的 super.dispatchTouchEvent(ev),事件将分发给本层的事件拦截onInterceptTouchEvent 方法进行处理。

如果事件分发返回 false,表明事件在本层不再继续进行分发,并交由上层控件的onTouchEvent方法

注意:Activity,没有事件拦截,将会把事件传递给该activity根布局容器的分发dispatchTouchEvent。

3、事件是从布局跟容器逐层向上传递的,直到事件被处理。


详细请看view层级结构图。

super.onTouchEvent(ev)源码

@Override

    publicboolean onTouchEvent(MotionEvent ev) {

       if (mFakeDragging) {

           // A fake drag is in progress already, ignore this real one

           // but still eat the touch events.

           // (It is likely that the user is multi-touching the screen.)

           return true;

       }

 

       if (ev.getAction() == MotionEvent.ACTION_DOWN&& ev.getEdgeFlags() != 0) {

           // Don't handle edge touches immediately -- they may actually belong toone of our

           // descendants.

           return false;

        }

 

       if (mAdapter == null || mAdapter.getCount() == 0) {

           // Nothing to present or scroll; nothing to touch.

           return false;

       }

 

       if (mVelocityTracker == null) {

           mVelocityTracker = VelocityTracker.obtain();

       }

       mVelocityTracker.addMovement(ev);

 

       final int action = ev.getAction();

       boolean needsInvalidate = false;

 

       switch (action & MotionEventCompat.ACTION_MASK){

           case MotionEvent.ACTION_DOWN: {

               mScroller.abortAnimation();

                mPopulatePending = false;

                populate();

 

                // Remember where the motionevent started

                mLastMotionX = mInitialMotionX= ev.getX();

                mLastMotionY = mInitialMotionY= ev.getY();

                mActivePointerId =MotionEventCompat.getPointerId(ev, 0);

                break;

           }

           case MotionEvent.ACTION_MOVE:

                if (!mIsBeingDragged) {

                    final int pointerIndex= MotionEventCompat.findPointerIndex(ev, mActivePointerId);

                    final float x= MotionEventCompat.getX(ev, pointerIndex);

                    final float xDiff= Math.abs(x - mLastMotionX);

                    final float y= MotionEventCompat.getY(ev, pointerIndex);

                    final float yDiff= Math.abs(y - mLastMotionY);

                    if (DEBUG)Log.v(TAG, "Moved x to " + x + "," + y+ " diff=" + xDiff + "," + yDiff);

                    if (xDiff >mTouchSlop && xDiff > yDiff) {

                        if (DEBUG)Log.v(TAG, "Starting drag!");

                        mIsBeingDragged = true;

                       requestParentDisallowInterceptTouchEvent(true);

                        mLastMotionX = x -mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :

                                mInitialMotionX- mTouchSlop;

                        mLastMotionY = y;

                        setScrollState(SCROLL_STATE_DRAGGING);

                        setScrollingCacheEnabled(true);

 

                        // Disallow ParentIntercept, just in case

                        ViewParent parent= getParent();

                        if (parent != null){

                           parent.requestDisallowInterceptTouchEvent(true);

                        }

                    }

                }

                // Not else! Note thatmIsBeingDragged can be set above.

                if (mIsBeingDragged) {

                    // Scroll to follow themotion event

                    final int activePointerIndex= MotionEventCompat.findPointerIndex(

                            ev,mActivePointerId);

                    final float x= MotionEventCompat.getX(ev, activePointerIndex);

                    needsInvalidate |=performDrag(x);

                }

                break;

           case MotionEvent.ACTION_UP:

                if (mIsBeingDragged) {

                    finalVelocityTracker velocityTracker = mVelocityTracker;

                   velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);

                    int initialVelocity= (int) VelocityTrackerCompat.getXVelocity(

                            velocityTracker,mActivePointerId);

                    mPopulatePending = true;

                    final int width= getClientWidth();

                    final int scrollX= getScrollX();

                    final ItemInfo ii= infoForCurrentScrollPosition();

                    final int currentPage= ii.position;

                    final float pageOffset= (((float) scrollX / width) - ii.offset) / ii.widthFactor;

                    final int activePointerIndex=

                            MotionEventCompat.findPointerIndex(ev,mActivePointerId);

                    final float x= MotionEventCompat.getX(ev, activePointerIndex);

                    final int totalDelta= (int) (x - mInitialMotionX);

                    int nextPage= determineTargetPage(currentPage, pageOffset, initialVelocity,

                            totalDelta);

                    setCurrentItemInternal(nextPage,true, true, initialVelocity);

 

                    mActivePointerId = INVALID_POINTER;

                    endDrag();

                    needsInvalidate =mLeftEdge.onRelease() | mRightEdge.onRelease();

                }

               break;

           case MotionEvent.ACTION_CANCEL:

                if (mIsBeingDragged) {

                    scrollToItem(mCurItem, true,0, false);

                    mActivePointerId = INVALID_POINTER;

                    endDrag();

                   needsInvalidate =mLeftEdge.onRelease() | mRightEdge.onRelease();

                }

                break;

           case MotionEventCompat.ACTION_POINTER_DOWN: {

                final int index= MotionEventCompat.getActionIndex(ev);

               final float x= MotionEventCompat.getX(ev, index);

                mLastMotionX = x;

                mActivePointerId =MotionEventCompat.getPointerId(ev, index);

                break;

           }

           case MotionEventCompat.ACTION_POINTER_UP:

                onSecondaryPointerUp(ev);

                mLastMotionX =MotionEventCompat.getX(ev,

                        MotionEventCompat.findPointerIndex(ev,mActivePointerId));

                break;

       }

       if (needsInvalidate) {

           ViewCompat.postInvalidateOnAnimation(this);

       }

       return true;

}

 

super.onInterceptTouchEvent(ev)源码

@Override

    public boolean onInterceptTouchEvent(MotionEventev) {

        /*

         * This method JUST determines whetherwe want to intercept the motion.

         * If we return true, onMotionEventwill be called and we do the actual

         * scrolling there.

         */

 

        final int action =ev.getAction() & MotionEventCompat.ACTION_MASK;

 

        // Always take care of the touchgesture being complete.

        if (action == MotionEvent.ACTION_CANCEL|| action == MotionEvent.ACTION_UP) {

           // Release the drag.

            if (DEBUG)Log.v(TAG, "Intercept done!");

            mIsBeingDragged = false;

            mIsUnableToDrag = false;

            mActivePointerId = INVALID_POINTER;

            if (mVelocityTracker != null){

                mVelocityTracker.recycle();

                mVelocityTracker = null;

            }

            return false;

        }

 

        // Nothing more to do here if we havedecided whether or not we

        // are dragging.

        if (action != MotionEvent.ACTION_DOWN){

            if (mIsBeingDragged) {

                if (DEBUG)Log.v(TAG, "Intercept returning true!");

                return true;

            }

            if (mIsUnableToDrag) {

                if (DEBUG)Log.v(TAG, "Intercept returning false!");

                return false;

            }

        }

 

        switch (action) {

            case MotionEvent.ACTION_MOVE:{

                /*

                 * mIsBeingDragged == false,otherwise the shortcut would have caught it. Check

                 * whether the user has movedfar enough from his original down touch.

                 */

 

                /*

                * Locally do absolute value.mLastMotionY is set to the y value

                * of the down event.

                */

                final int activePointerId= mActivePointerId;

                if (activePointerId == INVALID_POINTER){

                    // If we don't have a validid, the touch down wasn't on content.

                    break;

                }

 

                final int pointerIndex= MotionEventCompat.findPointerIndex(ev, activePointerId);

                final float x= MotionEventCompat.getX(ev, pointerIndex);

                final float dx= x - mLastMotionX;

                final float xDiff= Math.abs(dx);

                final float y= MotionEventCompat.getY(ev, pointerIndex);

                final float yDiff= Math.abs(y - mInitialMotionY);

                if (DEBUG)Log.v(TAG, "Moved x to " + x + "," + y+ " diff=" + xDiff + "," + yDiff);

 

                if (dx != 0 &&!isGutterDrag(mLastMotionX, dx) &&

                        canScroll(this, false,(int) dx, (int) x, (int) y)) {

                    // Nested view hasscrollable area under this point. Let it be handled there.

                    mLastMotionX = x;

                    mLastMotionY = y;

                    mIsUnableToDrag = true;

                    return false;

                }

                if (xDiff >mTouchSlop && xDiff * 0.5f > yDiff) {

                    if (DEBUG)Log.v(TAG, "Starting drag!");

                    mIsBeingDragged = true;

                   requestParentDisallowInterceptTouchEvent(true);

                    setScrollState(SCROLL_STATE_DRAGGING);

                    mLastMotionX = dx > 0 ?mInitialMotionX + mTouchSlop :

                            mInitialMotionX -mTouchSlop;

                    mLastMotionY = y;

                    setScrollingCacheEnabled(true);

                } else if (yDiff> mTouchSlop) {

                    // The finger has movedenough in the vertical

                    // direction to be countedas a drag...  abort

                    // any attempt to draghorizontally, to work correctly

                    // with children that havescrolling containers.

                    if (DEBUG) Log.v(TAG,"Starting unable to drag!");

                    mIsUnableToDrag = true;

                }

                if (mIsBeingDragged) {

                    // Scroll to follow themotion event

                    if (performDrag(x)){

                        ViewCompat.postInvalidateOnAnimation(this);

                    }

                }

                break;

            }

 

            case MotionEvent.ACTION_DOWN:{

                /*

                 * Remember location of downtouch.

                 * ACTION_DOWN always refers topointer index 0.

                 */

                mLastMotionX = mInitialMotionX= ev.getX();

                mLastMotionY = mInitialMotionY= ev.getY();

                mActivePointerId = MotionEventCompat.getPointerId(ev,0);

                mIsUnableToDrag = false;

 

               mScroller.computeScrollOffset();

                if (mScrollState == SCROLL_STATE_SETTLING&&

                        Math.abs(mScroller.getFinalX()- mScroller.getCurrX()) > mCloseEnough) {

                    // Let the user 'catch' thepager as it animates.

                    mScroller.abortAnimation();

                    mPopulatePending = false;

                    populate();

                    mIsBeingDragged = true;

                   requestParentDisallowInterceptTouchEvent(true);

                    setScrollState(SCROLL_STATE_DRAGGING);

                } else {

                    completeScroll(false);

                    mIsBeingDragged = false;

                }

 

                if (DEBUG)Log.v(TAG, "Down at " + mLastMotionX +"," + mLastMotionY

                        + "mIsBeingDragged=" + mIsBeingDragged

                        +"mIsUnableToDrag=" + mIsUnableToDrag);

                break;

            }

 

            case MotionEventCompat.ACTION_POINTER_UP:

                onSecondaryPointerUp(ev);

                break;

        }

 

        if (mVelocityTracker == null){

            mVelocityTracker = VelocityTracker.obtain();

        }

        mVelocityTracker.addMovement(ev);

 

        /*

         * The only time we want to interceptmotion events is if we are in the

         * drag mode.

         */

        return mIsBeingDragged;

    }

 

 

super.dispatchTouchEvent(ev)源码

    @Override

    public boolean dispatchTouchEvent(MotionEventev) {

        if(mInputEventConsistencyVerifier != null) {

           mInputEventConsistencyVerifier.onTouchEvent(ev, 1);

        }

 

        // If the event targets theaccessibility focused view and this is it, start

        // normal event dispatch. Maybe adescendant is what will handle the click.

        if(ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()){

            ev.setTargetAccessibilityFocus(false);

        }

 

        boolean handled = false;

        if(onFilterTouchEventForSecurity(ev)) {

            final int action= ev.getAction();

            final int actionMasked= action & MotionEvent.ACTION_MASK;

 

            // Handle an initial down.

            if (actionMasked ==MotionEvent.ACTION_DOWN) {

                // Throw away all previousstate when starting a new touch gesture.

                // The framework may havedropped the up or cancel event for the previous gesture

                // due to an app switch, ANR,or some other state change.

                cancelAndClearTouchTargets(ev);

                resetTouchState();

            }

 

            // Check for interception.

            final boolean intercepted;

            if (actionMasked ==MotionEvent.ACTION_DOWN

                    || mFirstTouchTarget != null){

                final boolean disallowIntercept= (mGroupFlags &FLAG_DISALLOW_INTERCEPT) != 0;

                if (!disallowIntercept){

                    intercepted =onInterceptTouchEvent(ev);

                    ev.setAction(action); // restoreaction in case it was changed

                } else {

                    intercepted = false;

                }

            } else {

                // There are no touch targetsand this action is not an initial down

                // so this view group continuesto intercept touches.

                intercepted = true;

            }

 

            // If intercepted, start normalevent dispatch. Also if there is already

            // a view that is handling the gesture,do normal event dispatch.

            if (intercepted ||mFirstTouchTarget != null) {

                ev.setTargetAccessibilityFocus(false);

            }

 

            // Check for cancelation.

            final boolean canceled= resetCancelNextUpFlag(this)

                    || actionMasked ==MotionEvent.ACTION_CANCEL;

 

            // Update list of touch targets forpointer down, if needed.

            final boolean split= (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;

            TouchTarget newTouchTarget =null;

            boolean alreadyDispatchedToNewTouchTarget= false;

            if (!canceled &&!intercepted) {

 

                // If the event is targetingaccessiiblity focus we give it to the

                // view that has accessibilityfocus and if it does not handle it

                // we clear the flag anddispatch the event to all children as usual.

                // We are looking up theaccessibility focused host to avoid keeping

                // state since these events arevery rare.

                View childWithAccessibilityFocus= ev.isTargetAccessibilityFocus()

                        ?findChildWithAccessibilityFocus() : null;

 

                if (actionMasked ==MotionEvent.ACTION_DOWN

                        || (split &&actionMasked == MotionEvent.ACTION_POINTER_DOWN)

                        || actionMasked ==MotionEvent.ACTION_HOVER_MOVE) {

                    final int actionIndex= ev.getActionIndex(); // always 0 for down

                    final int idBitsToAssign= split ? 1 << ev.getPointerId(actionIndex)

                            : TouchTarget.ALL_POINTER_IDS;

 

                    // Clean up earlier touchtargets for this pointer id in case they

                    // have become out of sync.

                    removePointersFromTouchTargets(idBitsToAssign);

 

                    final int childrenCount= mChildrenCount;

                    if (newTouchTarget== null && childrenCount != 0) {

                        final floatx = ev.getX(actionIndex);

                        final floaty = ev.getY(actionIndex);

                        // Find a child thatcan receive the event.

                        // Scan children fromfront to back.

                        finalArrayList<View> preorderedList = buildOrderedChildList();

                        final booleancustomOrder = preorderedList ==null

                                &&isChildrenDrawingOrderEnabled();

                        final View[] children= mChildren;

                        for (int i= childrenCount - 1; i >= 0; i--) {

                            final intchildIndex = customOrder

                                    ?getChildDrawingOrder(childrenCount, i) : i;

                            final View child= (preorderedList ==null)

                                    ?children[childIndex] : preorderedList.get(childIndex);

 

                            // If there is aview that has accessibility focus we want it

                            // to get the eventfirst and if not handled we will perform a

                            // normal dispatch.We may do a double iteration but this is

                            // safer given thetimeframe.

                            if(childWithAccessibilityFocus !=null) {

                                if(childWithAccessibilityFocus != child) {

                                    continue;

                                }

                               childWithAccessibilityFocus = null;

                                i =childrenCount - 1;

                            }

 

                            if (!canViewReceivePointerEvents(child)

                                    ||!isTransformedTouchPointInView(x, y, child,null)) {

                               ev.setTargetAccessibilityFocus(false);

                                continue;

                            }

 

                            newTouchTarget =getTouchTarget(child);

                            if(newTouchTarget != null) {

                                // Child isalready receiving touch within its bounds.

                                // Give it thenew pointer in addition to the ones it is handling.

                               newTouchTarget.pointerIdBits |= idBitsToAssign;

                                break;

                            }

 

                            resetCancelNextUpFlag(child);

                            if(dispatchTransformedTouchEvent(ev,false, child, idBitsToAssign)) {

                                // Child wantsto 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;

                            }

 

                            // Theaccessibility focus didn't handle the event, so clear

                            // the flag and doa normal dispatch to all children.

                           ev.setTargetAccessibilityFocus(false);

                        }

                        if(preorderedList != null) preorderedList.clear();

                    }

 

                    if (newTouchTarget== null && mFirstTouchTarget !=null) {

                        // Did not find a childto receive the event.

                        // Assign the pointerto the least recently added target.

                        newTouchTarget =mFirstTouchTarget;

                        while (newTouchTarget.next != null){

                            newTouchTarget =newTouchTarget.next;

                        }

                       newTouchTarget.pointerIdBits |= idBitsToAssign;

                    }

                }

            }

 

            // Dispatch to touch targets.

            if (mFirstTouchTarget == null){

                // No touch targets so treatthis 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 {

                        final booleancancelChild = 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;

                }

            }

 

            // Update list of touch targets forpointer up or cancel, if needed.

            if (canceled

                    || actionMasked ==MotionEvent.ACTION_UP

                    || actionMasked ==MotionEvent.ACTION_HOVER_MOVE) {

                resetTouchState();

            } else if (split&& actionMasked == MotionEvent.ACTION_POINTER_UP) {

                final int actionIndex= ev.getActionIndex();

                final int idBitsToRemove= 1 << ev.getPointerId(actionIndex);

               removePointersFromTouchTargets(idBitsToRemove);

            }

        }

 

        if (!handled &&mInputEventConsistencyVerifier != null) {

           mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);

        }

        return handled;

    }

布局截图和层级截图:


0 0
原创粉丝点击