事件分发
来源:互联网 发布:scrollreveal.js 教程 编辑:程序博客网 时间:2024/05/30 23:38
我们直接对比ViewGroup和view的dispatchTouchEvent方法来理解touch事件的分发。
如下是ViewGroup的dispatchTouchEvent方法:
//ViewGroup中的dispatchTouchEventpublic 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行),
如果onInterceptTouchEvent返回为false,也就是不拦截的话,会把此touch事件分发给此viewgroup的所有子view(37行);在此情况之下,如果所有的子view的dispatchTouchEvent返回的都是false,那么mMotionTarget为null,就会执行到64行,super.dispatchTouchEvent(ev),也就是View中的dispatchTouchEvent方法了,因为所有的ViewGroup的父类都是View,view的dispatchTouchEvent可以参考第二个方法,如果有子view的dispatchTouchEvent方法返回true,那么就把他赋值给mMotionTarget,那么下次move和up事件来的时候直接交给其处理(91行)
如果onInterceptTouchEvent返回为true,也就是ViewGroup拦截此次touch事件,那么就不会进入if判断语句的内部,而会执行到64行。
总结来说,如果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
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- 事件分发
- android shape的使用
- 多用块枚举,少用for循环
- 礼拜一log~java Map & 层级筛选代码
- Linux基础总结
- 【Java80小白建站系列】7.系列搬家至www.java80.com/blog/index.htm
- 事件分发
- 数据库分库分表(sharding)系列(一) 拆分实施策略和示例演示
- Android 二维码扫描基于Google Zxing(仿微信)
- iOS NSClassFromString 详解
- 告别数据线,使用无线进行真机测试
- c 文件拷贝 fopen fwrite fread
- 深入Java集合系列之三:HashMap
- HTML文字与段落标记
- Android属性动画完全解析(上),初识属性动画的基本用法