事件分发

     我们直接对比ViewGroup和view的dispatchTouchEvent方法来理解touch事件的分发。

如下是ViewGroup的dispatchTouchEvent方法:

 

//ViewGroup中的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;
	    //如果是ACTION_DOWN这个动作的话,就会mMotionTarget设为null,来重新寻找目标view
	    if (action == MotionEvent.ACTION_DOWN) {
	        if (mMotionTarget != null) {
	            mMotionTarget = null;
	        }
	        //disallowIntercept是否禁用拦截功能,onInterceptTouchEvent:是否拦截
	        //1.如果禁用了拦截即disallowIntercept为true,或者没有禁用,但是没有拦截,
	        //就会进入if之内寻找需要消费此touch事件的子view了
	        //2.如果拦截了,则不会进入此if中,则直接到64行执行
	        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;
	                      //如果此处有其中一个子view的dispatchTouchEvent返回true,那么就把
	                      //此子view当做mMotionTarget
	                        if (child.dispatchTouchEvent(ev))  {
	                            mMotionTarget = child;
	                            return true;
	                        }
	                    }
	                }
	            }
	        }
	    }//end action == MotionEvent.ACTION_DOWN
	    
	    
	    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;
	        }
	        //如果第37行没有成立,即当前viewgroup的所有子view的dispatchTouchEvent
	        //都返回false,那么就是没找到mMotionTarget,即mMotionTarget为null,在
	        //此情况下就会执行此句,也是View中的dispatchTouchEvent方法了,因为所有的
	        //ViewGroup的父类都是View,view的dispatchTouchEvent可以参考下一个方法。
	        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;
	    }
	    //如果mMotionTarget不为空,那么move和up事件直接交给其处理
	    return target.dispatchTouchEvent(ev);
	}

 

 

如下是View的dispatchTouchEvent方法:

 

 

 

	/* 此处为view的dispatchTouchEvent方法
	 * 如果此方法返回true,那么如上ViewGroup中的dispatchTouchEvent方法
	 * 的mMotionTarget就不为空了
	 * 
	 * */
	public boolean dispatchTouchEvent(MotionEvent event) {
	    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
	            mOnTouchListener.onTouch(this, event)) {
	        return true;
	    }
	    return onTouchEvent(event);
	}

 

 

 

通过如上源码可以了解到,在viewgroup的dispatchTouchEvent方法的调用流程中,是先会去调用onInterceptTouchEvent方法(19行),

 

 

 

如果不允许拦截(disallowIntercept为true)或者是允许拦截(disallowIntercept为false)但不拦截(onInterceptTouchEvent返回false)的情况下,会把此touch事件分发给此viewgroup的所有子view(37行);在此情况之下,如果所有的子view的dispatchTouchEvent返回的都是false,那么mMotionTarget为null,就会执行到64行,super.dispatchTouchEvent(ev),也就是View中的dispatchTouchEvent方法了,因为所有的ViewGroup的父类都是View,所以64行表示viewgroup自己消耗了touch事件,怎么消耗的参考view的dispatchTouchEvent方法;如果有子view的dispatchTouchEvent方法返回true,那么就把他赋值给mMotionTarget,那么下次move和up事件来的时候直接交给其处理(91行)

如果onInterceptTouchEvent返回为true,也就是ViewGroup拦截此次touch事件,那么就不会进入if判断语句的内部,而会执行到64行,viewgroup自己来消耗touch事件。

 

总结来说,如果onInterceptTouchEvent为false但是所有的子view的dispatchTouchEvent都返回false,或者是直接拦截,在这两种情况之下,都会执行到64行,super.dispatchTouchEvent(ev),而此行也就是父类的dispatchTouchEvent方法,参考方法二。

 

 

看如下伪代码:

 

View mTarget=null;//保存捕获Touch事件处理的View
    public boolean dispatchTouchEvent(MotionEvent ev) {

        //....其他处理,在此不管
        
        if(ev.getAction()==KeyEvent.ACTION_DOWN){
            //每次Down事件,都置为Null

            if(!onInterceptTouchEvent()){
            mTarget=null;
            View[] views=getChildView();
            for(int i=0;i<views.length;i++){
                if(views[i].dispatchTouchEvent(ev))
                    mTarget=views[i];
                    return true;
            }
          }
        }
        //当子View没有捕获down事件时,ViewGroup自身处理。
        //此时把此ViewGroup当做一个view来看待
        //这里处理的Touch事件包含Down、Up和Move
        if(mTarget==null){
            return super.dispatchTouchEvent(ev);
        }
        //...其他处理,在此不管
        if(onInterceptTouchEvent()){

         //...其他处理,在此不管    
         }
         //当子View捕获了down事件时,mTarget不为空,那么Move和UP直接交给mTarget处理
				//这一步在Action_Down中是不会执行到的,只有Move和UP才会执行到。
        return mTarget.dispatchTouchEvent(ev);

    }

此处的伪代码是viewgroup的dispatchTouchEvent方法的简化,可以很清楚的看到有子view消费事件和没有子view消费事件的流程
 

 

参考如下资料:

http://www.cnblogs.com/linjzong/p/4191891.html

http://blog.csdn.net/guolin_blog/article/details/9097463

http://blog.csdn.net/lmj623565791/article/details/39102591

同时可以验证

http://blog.csdn.net/chdjj/article/details/22910581

滑动冲突的案例

https://blog.csdn.net/gdutxiaoxu/article/details/52939127

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值