Android 触摸消息派发之ViewGroup的派发过程

来源:互联网 发布:数据库实训报告 编辑:程序博客网 时间:2024/09/21 09:22

Android系统中一个完整的触摸消息过程为ACTION_DOWN,然后是ACTION_MOVE,最后是ACTION_UP。整个过程中涉及的函数有dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent。

当触摸消息派发到ViewGroup时会调用dispatchTouchEvent方法,它负责整个触摸消息的处理。在dispatchTouchEvent方法中首先获取触摸消息的类型以及触摸点的位置,如果消息类型为ACTION_DOWN时,首先判断mMotionTarget是否为空,mMotionTarget保存的是上次触摸消息的处理者,一般在ACTION_DOWN情况下mMotionTarget为null,原因将会在后面看到,至于在ACTION_DOWN情况下mMotionTarget不为null,我也不太清楚啊(嘿嘿)。

然后是判断是否允许拦截消息或是拦截过程中是否消耗了这个消息,if判断成立的条件一是ViewGroup被设置不允许拦截触摸消息,方法是通过调用requestDisallowInterceptTouchEvent(boolean disallowIntercept),另一种情况是onInterceptTouchEvent返回false,在ViewGroup中该方法的默认实现为return false。当if条件成立时,从ViewGroup中包含的所有子View中判断该触摸消息的位置落在哪个View中,然后调用该View的dispatchTouchEvent将消息传递下去。如果子View消耗了该消息则将该View赋值给mMotionTarget变量,记录消息的处理者以便在后续的触摸消息中使用,最后函数返回true一次消息派发结束,后续的消息为ACTION_MOVE或ACTION_UP。如果子View没有消耗该消息则代码接着往下执行,代码第71行判断mMotionTarget是否为null,如果为null的话说明在ACTION_DOWN的情况下没有子View消耗该消息,所以触摸消息由ViewGroup本身处理,super.dispatchTouchEvent即调用View的dispatchTouchEvent处理。所以当触摸消息为ACTON_DOWN时可能从两个地方返回,一是代码第49行返回,另一个是从代码第80行返回。

如果消息类型为ACTION_UP或是ACTION_CANCEL,从代码85行开始首先判断在允许拦截消息并且onInterceptTouchEvent返回true的情况下表明ViewGroup本身把消息消耗了,所以就没有子View什么事了,所以后面将MotionEvent设置为ACTION_CANCEL类型,调用原来mMotionTarget的dispatchTouchEvent方法通知它触摸取消,以供子View取消长按监听等处理。然后将mMotionTarget设置为null,同时返回true,这样从ACTION_DOWN到ACTION_CANEL或ACTION_UP一个完整的触摸过程就结束了。

但是如果代码第85行ViewGroup本身没有消耗该消息,则直接调用mMotionTarget的dispatchTouchEvent方法将消息传递下去。如果mMotionTarget为ViewGroup类型则递归这个过程,如果mMotionTarget为View类型则

ViewGroup中dispatchTouchEvent函数执行过程

    public boolean dispatchTouchEvent(MotionEvent ev) {        if (!onFilterTouchEventForSecurity(ev)) {            return false;        }        final int action = ev.getAction();        final float xf = ev.getX();        final float yf = ev.getY();        final float scrolledXFloat = xf + mScrollX;        final float scrolledYFloat = yf + mScrollY;        final Rect frame = mTempRect;        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;        if (action == MotionEvent.ACTION_DOWN) {            if (mMotionTarget != null) {                // this is weird, we got a pen down, but we thought it was                // already down!                // XXX: We should probably send an ACTION_UP to the current                // target.                mMotionTarget = null;            }            // If we're disallowing intercept or if we're allowing and we didn't            // intercept            if (disallowIntercept || !onInterceptTouchEvent(ev)) {                // reset this event's action (just to protect ourselves)                ev.setAction(MotionEvent.ACTION_DOWN);                // We know we want to dispatch the event down, find a child                // who can handle it, start with the front-most child.                final int scrolledXInt = (int) scrolledXFloat;                final int scrolledYInt = (int) scrolledYFloat;                final View[] children = mChildren;                final int count = mChildrenCount;                for (int i = count - 1; i >= 0; i--) {                    final View child = children[i];                    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE                            || child.getAnimation() != null) {                        child.getHitRect(frame);                        if (frame.contains(scrolledXInt, scrolledYInt)) {                            // offset the event to the view's coordinate system                            final float xc = scrolledXFloat - child.mLeft;                            final float yc = scrolledYFloat - child.mTop;                            ev.setLocation(xc, yc);                            child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;                            if (child.dispatchTouchEvent(ev))  {                                // Event handled, we have a target now.                                mMotionTarget = child;                                return true;                            }                            // The event didn't get handled, try the next view.                            // Don't reset the event's location, it's not                            // necessary here.                        }                    }                }            }        }        boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||                (action == MotionEvent.ACTION_CANCEL);        if (isUpOrCancel) {            // Note, we've already copied the previous state to our local            // variable, so this takes effect on the next event            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;        }        // The event wasn't an ACTION_DOWN, dispatch it to our target if        // we have one.        final View target = mMotionTarget;        if (target == null) {            // We don't have a target, this means we're handling the            // event as a regular view.            ev.setLocation(xf, yf);            if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {                ev.setAction(MotionEvent.ACTION_CANCEL);                mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;            }            return super.dispatchTouchEvent(ev);        }        // if have a target, see if we're allowed to and want to intercept its        // events        if (!disallowIntercept && onInterceptTouchEvent(ev)) {            final float xc = scrolledXFloat - (float) target.mLeft;            final float yc = scrolledYFloat - (float) target.mTop;            mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;            ev.setAction(MotionEvent.ACTION_CANCEL);            ev.setLocation(xc, yc);            if (!target.dispatchTouchEvent(ev)) {                // target didn't handle ACTION_CANCEL. not much we can do                // but they should have.            }            // clear the target            mMotionTarget = null;            // Don't dispatch this event to our own view, because we already            // saw it when intercepting; we just want to give the following            // event to the normal onTouchEvent().            return true;        }        if (isUpOrCancel) {            mMotionTarget = null;        }        // finally offset the event to the target's coordinate system and        // dispatch the event.        final float xc = scrolledXFloat - (float) target.mLeft;        final float yc = scrolledYFloat - (float) target.mTop;        ev.setLocation(xc, yc);        if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {            ev.setAction(MotionEvent.ACTION_CANCEL);            target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;            mMotionTarget = null;        }        return target.dispatchTouchEvent(ev);    }
0 0
原创粉丝点击