Android事件传递

来源:互联网 发布:淘宝买家退货率高 编辑:程序博客网 时间:2024/06/05 07:37

事件分发:dispatchTouchEvent

事件拦截:onInterceptTouchEvent

事件消费:onTouchEvent

public static final int ACTION_DOWN = 0;
public static final int ACTION_UP = 1;
public static final int ACTION_MOVE = 2;

这里写图片描述
MainActivity:
–>boolean dispatchTouchEvent()

public boolean dispatchTouchEvent(MotionEvent ev) {    if (ev.getAction() == MotionEvent.ACTION_DOWN) {        onUserInteraction();//这里是一个空方法    }    if (getWindow().superDispatchTouchEvent(ev)) {        return true;    }    return onTouchEvent(ev);}

PhoneWindow:

    -->boolean superDispatchTouchEvent()

DecorView:这里DecorView就是一个FrameLayout,所以他的父类就是ViewGroup

    -->boolean superDispatchTouchEvent()

ViewGroup:
–>boolean dispatchTouchEvent()

@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {    ......    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 previous state when starting a new touch gesture.            // The framework may have dropped the up or cancel event for the previous gesture            // due to an app switch, ANR, or some other state change.            cancelAndClearTouchTargets(ev);            //清除FLAG_DISALLOW_INTERCEPT设置并且mFirstTouchTarget设置为null            resetTouchState();        }        // Check for interception.        final boolean intercepted;        if (actionMasked == MotionEvent.ACTION_DOWN                || mFirstTouchTarget != null) {            //requestDisallowInterceptTouchEvent方法进行设置的            //子控件是否禁止父容器拦截事件            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;            if (!disallowIntercept) {                //由子控件决定是否拦截                intercepted = onInterceptTouchEvent(ev);                ev.setAction(action); // restore action in case it was changed            } else {                //子控件禁止父容器拦截事件的时候,没有必要判断是否拦截                intercepted = false;            }        } else {            //当事件不为ACTION_DOWN且事件由ViewGroup自己处理时,没有必要判断是否拦截            // There are no touch targets and this action is not an initial down            // so this view group continues to intercept touches.            intercepted = true;        }        // If intercepted, start normal event 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 for pointer 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 targeting accessiiblity focus we give it to the            // view that has accessibility focus and if it does not handle it            // we clear the flag and dispatch the event to all children as usual.            // We are looking up the accessibility focused host to avoid keeping            // state since these events are very 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 touch targets for this pointer id in case they                // have become out of sync.                removePointersFromTouchTargets(idBitsToAssign);                final int childrenCount = mChildrenCount;                //没有控件消费了事件且子控件数量不为0                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.                    //将list排序,查找的时候先查找在屏幕上层的控件                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();                    final boolean customOrder = preorderedList == null                            && isChildrenDrawingOrderEnabled();                    final View[] children = mChildren;                    //倒着遍历,先从屏幕最上方的控件开始                    for (int i = childrenCount - 1; i >= 0; i--) {                        final int childIndex = getAndVerifyPreorderedIndex(                                childrenCount, i, customOrder);                        //找到相应下标的view                        final View child = getAndVerifyPreorderedView(                                preorderedList, children, childIndex);                        // If there is a view that has accessibility focus we want it                        // to get the event first and if not handled we will perform a                        // normal dispatch. We may do a double iteration but this is                        // safer given the timeframe.                        if (childWithAccessibilityFocus != null) {                            if (childWithAccessibilityFocus != child) {                                continue;                            }                            childWithAccessibilityFocus = null;                            i = childrenCount - 1;                        }                        //child没有执行动画且点击的范围在child的范围内                        //以上两个条件有一个不满足,就continue去看下一个View                        if (!canViewReceivePointerEvents(child)                                || !isTransformedTouchPointInView(x, y, child, null)) {                            ev.setTargetAccessibilityFocus(false);                            continue;                        }                        newTouchTarget = getTouchTarget(child);                        if (newTouchTarget != null) {                            // Child is already receiving touch within its bounds.                            // Give it the new pointer in addition to the ones it is handling.                            newTouchTarget.pointerIdBits |= idBitsToAssign;                            break;                        }                        resetCancelNextUpFlag(child);                        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;                        }                        // The accessibility focus didn't handle the event, so clear                        // the flag and do a normal dispatch to all children.                        ev.setTargetAccessibilityFocus(false);                    }                    if (preorderedList != null) preorderedList.clear();                }                if (newTouchTarget == null && mFirstTouchTarget != null) {                    // Did not find a child to receive the event.                    // Assign the pointer to 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 treat this as an ordinary view.            //这里第三个参数为null            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 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;            }        }    ......       return handled;}

在里面调用了dispatchTransformedTouchEvent()

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,                                              View child, int desiredPointerIdBits) {    final boolean handled;    ......    // Perform any necessary transformations and dispatch.    if (child == null) {        handled = super.dispatchTouchEvent(transformedEvent);    } else {        final float offsetX = mScrollX - child.mLeft;        final float offsetY = mScrollY - child.mTop;        transformedEvent.offsetLocation(offsetX, offsetY);        if (!child.hasIdentityMatrix()) {            transformedEvent.transform(child.getInverseMatrix());        }        handled = child.dispatchTouchEvent(transformedEvent);    }    // Done.    transformedEvent.recycle();    return handled;}

调用了父类的或者子类重写了的dispatchTouchEvent

View:
–>boolean dispatchTouchEvent()

    public boolean dispatchTouchEvent(MotionEvent event) {    ......    //窗口没有被遮盖    if (onFilterTouchEventForSecurity(event)) {        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {            result = true;        }        //noinspection SimplifiableIfStatement        //如果设置了onTouch事件,且返回true,那么就不在执行onTouchEvent了        ListenerInfo li = mListenerInfo;        if (li != null && li.mOnTouchListener != null                && (mViewFlags & ENABLED_MASK) == ENABLED                && li.mOnTouchListener.onTouch(this, event)) {            result = true;        }        //如果onTouchListener返回true,就不会走onTouchEvent了        if (!result && onTouchEvent(event)) {            result = true;        }    }    ......    return result;}

–>boolean onTouchEvent()

    public boolean onTouchEvent(MotionEvent event) {    ......    if (((viewFlags & CLICKABLE) == CLICKABLE ||            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||            (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {        switch (action) {            case MotionEvent.ACTION_UP:                ......                        // Only perform take click actions if we were in the pressed state                        if (!focusTaken) {                            // Use a Runnable and post this rather than calling                            // performClick directly. This lets other visual state                            // of the view update before click actions start.                            if (mPerformClick == null) {                                mPerformClick = new PerformClick();                            }                            if (!post(mPerformClick)) {                            //这里回调了我们的onClick事件                                performClick();                            }                        }                    }                ......    return false;}

–>boolean performClick()

public boolean performClick() {    final boolean result;    final ListenerInfo li = mListenerInfo;    if (li != null && li.mOnClickListener != null) {        playSoundEffect(SoundEffectConstants.CLICK);        li.mOnClickListener.onClick(this);        result = true;    } else {        result = false;    }    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);    return result;}

这里写图片描述

总结一下

  • dispatchTouchEvent是事件分发

    如果返回true,那么整个事件就停止了,不会再走其他的方法;如果返回false,就会直接回调父控件的onTouchEvent事件;如果返回了super.dispatchTouchEvent,就将事件向下传递
  • onInterceptTouchEvent是事件拦截


    View没有拦截方法,ViewGroup才有
    在viewGroup的dispatchTouchEvent返回super的时候会执行

    如果返回true,表示我要拦截事件,事件将不再向下传递,而是直接走自己的onTouchEvent方法如果返回false或者super,则会向下传递
  • onTouchEvent是事件消费

    如果返回true,表示我要消费,事件到此中断如果返回false或者super,都会向上传递,交给子控件消费
  • 如果子控件不想被父容器拦截,可以调用requestDisallowInterceptTouchEvent(true);

  • 我们都知道如果给一个控件注册了onTouch事件,每次点击都会出发Down,Move,Up等事件.这里要注意一点,如果在Down事件里返回了false,后面的一系列事件都不会执行了。意思就是,在dispatchTouchEvent事件分发的时候,只有前一个action返回了true,才会执行下一个action.

  • onTouch和onTouchEvent的区别

    从源码中可以看出,onTouch和onTouchEvent都是在view的dispatchTouchEvent中回调的,onTouch优于onTouchEvent,如果设置了onTouchListener,并且在onTouch中返回了true,就不会在走onTouchEvent了,这里要注意一下,onTouch执行的两个条件,一个是设置了onTouchListener,另外一个是控件必须是enable的,否则不会执行。比如ImageView,可以设置android:clickable="true",使得可以点击
原创粉丝点击