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

    xiaoxiao2023-03-24  3

    每个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;

        }

    布局截图和层级截图:

    转载请注明原文地址: https://ju.6miu.com/read-1201494.html
    最新回复(0)