Android事件分发机制完全解析

在阅读本文前,请先参考以下三篇文章

Android-onInterceptTouchEvent()和onTouchEvent()总结

Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

Android事件分发机制完全解析,带你从源码的角度彻底理解(下)


在读完上面三篇文章以后,相信大家对事件分发机制有了一个比较清晰的认识,为了更好地理解事件分发机制,我在这里总结了四个问题,并给出了本人的见解


1、touch事件由父控件向子控件一层一层的传递,这个过程是怎么发生的?

2、ViewGroup.onInterceptTouchEvent(ev)返回true的时候,ViewGroup是如何拦截touch事件的?

3、view.onTouchEvent(ev)返回false时,该touch事件是如何向其父控件传递的?

4、view.onTouchEvent(ev)返回true时,该view是如何消费该touch事件的?

5、若在action_down时,view没有消费该事件,那么后续的action_up、action_move,该view都不能捕获到,这是为什么?


1、touch事件由父控件向子空间一层一层的传递,这个过程是怎么发生的?

我们知道当一个view接收一个touch事件时,首先会调用dispatchTouchEvent(ev),通过阅读viewGoup.dispatchTouchEvent(ev)源码来解释第一个问题

public boolean dispatchTouchEvent(MotionEvent ev) {
    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) {
            mMotionTarget = null;
        }
        if (disallowIntercept || !onInterceptTouchEvent(ev)) {
            ev.setAction(MotionEvent.ACTION_DOWN);
            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)) {
                        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))  {
                            mMotionTarget = child;
                            return true;
                        }
                    }
                }
            }
        }
    }
    boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
            (action == MotionEvent.ACTION_CANCEL);
    if (isUpOrCancel) {
        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
    }
    final View target = mMotionTarget;
    if (target == null) {
        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 (!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)) {
        }
        mMotionTarget = null;
        return true;
    }
    if (isUpOrCancel) {
        mMotionTarget = null;
    }
    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);
}
注意29行,child.dispachTouchEvent(ev),通过这行代码父控件将touch事件传给了子控件,同样的子控件若也是一个ViewGroup且不对该事件进行拦截的话,它也会通过同样的方式将该touch事件传递给它的子控件。直到该事件传递给某个ViewGroup且被拦截,或者接受该事件的是一个view,该事件停止向下传递。


2、ViewGroup.onInterceptTouchEvent(ev)返回true的时候,ViewGroup是如何拦截touch事件的?

这个问题依然需要到viewGoup.dispatchTouchEvent(ev)去找答案,当onInterceptTouchEvent(ev)返回true时,13-37都不会执行,由于29行没有执行到,所以该touch事件并没有向下传递。


3、view.onTouchEvent(ev)返回false时,该touch事件是如何向其父控件传递的?

假设A是B的父控件,A将touch事件传递给了B,B执行dispatchTouchEvent(ev)——onTouchEvent(ev),由于onTouchEvent(ev)返回了false,所以B.dispatchTouchEvent(ev)也返回false,这样导致A.dispatchTouchEvent(ev)中的第30-37行不会执行,由于mMotionTarget=null,所以44行的if代码块会执行,代码末尾的super.dispatchTouchEvent(ev),即View的dispatchTouchEvent(ev),源码如下:

public boolean dispatchTouchEvent(MotionEvent event) {
    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
            mOnTouchListener.onTouch(this, event)) {
        return true;
    }
    return onTouchEvent(event);
}
第6行的onToucEvent(ev)的到了执行,这样B就将touch事件传递给了A,如果A的onTouchEvent(ev)返回false,它会以同样的方式将该touch事件传递给上一层的父控件

4、view.onTouchEvent(ev)返回true时,该view是如何消费该touch事件的?

同样的例子,假设A是B的父控件,A将touch事件传递给了B,B执行dispatchTouchEvent(ev)——onTouchEvent(ev),由于onTouchEvent(ev)返回了true,所以B.ispatchTouchEvent(ev)也返回true,这样A.dispatchToucEvent(ev)中的第29行的if代码块会执行,将A的mMotionTarget设为B,并且返回true,同样的A的父控件的dispatchToucEvent(ev)的第29行if代码块将其mMotionTaret赋值为A并返回true,这个过程一直持续到最顶层的容器。这样只有B执行了onTouchEvent(ev),A及更上一层的所有控件都没执行onTouchEvent(ev)。


5、若在action_down时,view没有消费该事件,那么后续的action_up、action_move,该view都不能捕获到,这是为什么?

同样的例子,假设A是B的父控件,A将touch事件传递给了B,由于B没有消费该事件,所以A中的第29行的if代码块不会执行,mMotionTarget=null。当下一个事件action_up或者action_move传递到A时,会从第8行跳到第38行,我们看重点44行的if代码块会执行,并返回A.onTouchEvent(ev)的返回值。返回true控件A消费该事件,返回false控件A将该touch事件传递给A的父控件。B并没有接收该touch事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值