Android Touch事件分发机制

             Android Touch事件的分发机制一直没有仔细去看。今天以一个例子总结一下。

        用这样一个情形来讲下,一个LinearLayout A 里面嵌套一个LinearLayout B,然后这个B里面有个一个Button C。用户点击Button C的触发流程以及各种情况的分析如下:

        View的分发机制毫无疑问是从根View至子View进行分发。而View事件的分发是由dispatchTouchEvent来进行的。所以这时候会首先走A(继承于ViewGroup)的dispatchTouchEvent,下面贴出dispatchTouchEvent的源代码:

        

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);
}
可以看出dispatchTouchEvent方法里会有一个 onInterceptTouchEvent方法。对于这个方法的理解是这样的,如果这个方法返回true,表明这个view将事件进行拦截了,这样事件就不会再往子view进行传递。然后就会执行的是这个view的onTouchEvent方法。如果onInterceptTouchEvent方法返回false,则dispachTouchEvent事件会往子View进行传递,也就是会执行子View的dispachTouchEvent方法。这个子View也是会进行和父View一样的逻辑处理。这些事件的传递要么会在某层View被拦截然后执行某层View的onTouchEvent事件;要么会一直进行传递一直到一个没有子View属性的View,比如说TextView、Button等。然后会执行这个子View的dispachTouchEvent方法。对于Button这样的View,其本身是没有dispachTouchEvent方法,一直追溯到其父类View的代码会有dispachTouchEvent方法如下:

public boolean dispatchTouchEvent(MotionEvent event) {
    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
            mOnTouchListener.onTouch(this, event)) {
        return true;
    }
    return onTouchEvent(event);
}
如果子View没有设置onTouch事件就会走子View的onTouchEvent事件,否则会直接返回true。

如果子View返回false,也就是子View 的dispachTouchEvent返回false,则会将事件向上传递,然后执行分发View的onTouchEvent事件。

对开头的例子有以下几种情况:

1、A 的dispatchTouchEvent肯定会被执行,然后接着执行A的onInterceptTouchEvent,如果onInterceptTouchEvent返回true,则A对事件进行了拦截,执行A的onTouchEvent事件。事件不会再往下传递;如果返回false,则事件会传递到B,

2、B的dispatchTouchEvent会进行执行,然后接着执行B的onInterceptTouchEvent,如果返回为true,则B对这个事件进行了拦截,会执行B的onTouchEvent事件。如果返回false,则事件继续往下传递到C。

3、C是一个原子View,也就是不能包含子View的View,所以其dispatchTouchEvent的实现和ViewGroup不一样。如果C设置了onTouch事件,则C的dispatchTouchEvent直接返回true,事件结束。这时候也不会执行C的onTouchEvent事件,因为onClick事件是在onTouchEvent方法里执行,所以onClick事件也不会执行。如果C的dispatchTouchEvent返回false,会执行父View的super.dispatchTouchEvent,其实也就是View的dispatchTouchEvent,这时候也就是会执行B的onTouchEvent方法,如果这时候B的dispatchTouchEvent也返回false,则会回到A的dispatchTouchEvent方法。如果A也返回false,那就到Activity的onTouchEvent了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值