事件分发解惑

当手指触摸屏幕的时候,首先事件来到ActivitydispatchTouchEvent方法,看下源码里的这个方法:

  public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
   }

  如果是按下事件,调用onUserInteraction方法,这个方法的方法体为空。

  接着是调用getWindow().superDispatchTouchEvent(ev),如果返回值为true,则dispatchTouchEvent方法返回也就为true,下面的onTouchEvent方法就不会执行了。这里的getWindow(),其实获取到的是一个PhoneWindow对象,往这个类里面看看:

    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
}

调用了DecorViewsuperDispatchTouchEvent(),最后又调用了ViewGroup的dispatchTouchEvent方法,所以这整个过程就是Activity-->PhoneWindow-->DecorView-->ViewGroup

   事件分发主要看这三个方法:dispatchTouchEvent()  用于分发事件

                                       onInterceptTouchEvent()  事件拦截

                                       onTouchEvent()  事件响应

 这三个方法返回值代表的涵义大致如图


   先从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.
                        }
                    }
                }
            }
        }
    看这一句代码:if (disallowIntercept || !onInterceptTouchEvent(ev))  如果disallowIntercept 或者 !onInterceptTouchEvent(ev)的值有一个为true就进入里面的代码,这里disallowIntercept表示不允许拦截,这个值默认为false,可以通过requestDisallowInterceptTouchEvent方法来设置。而onInterceptTouchEvent(ev)方法我们知道,默认返回也是false的,这个方法如果设置返回true就代表这个事件我拦截下来了,就不会往下面传递了。

  if (child.dispatchTouchEvent(ev))  {
  // Event handled, we have a target now.
  mMotionTarget = child;
  return true;
  }

 这里调用了子View的dispatchTouchEvent(ev),如果它返回true,那本ViewGroup的dispatch就返回true,下面的onTouchEvent方法就不会收到事件了,所以就说明了我们平时如果在一个布局里嵌套子View,点击子View的部分只响应子View的触摸事件和点击事件,而父View的就不会再触发。

  那如果点击的不是子View部分呢,看这部分代码 :

        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);
        }
  就会调用super.dispatchTouchEvent(ev),而super也就是View类,意思也就是按照本身的触摸事件来执行了。

 View的dispatchTouchEvent方法:

    public boolean dispatchTouchEvent(MotionEvent event) {
        if (!onFilterTouchEventForSecurity(event)) {
            return false;
        }

        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
                mOnTouchListener.onTouch(this, event)) {
            return true;
        }
        return onTouchEvent(event);
    }
  可以看到是调用了本身的onTouch()或者onTouchEvent方法。


  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值