1. dispatchTouchEvent :负责事件分发,由外层向里层传递。
2. onInterceptTouchEvent :只有ViewGroup有,可以拦截向下传递的事件。
3. requestDisallowInterceptTouchEvent:由孩子调用,可以阻止父Viewgroup拦截事件。
如上所述:第2和第3个函数是矛盾的,要是同时使用,哪个为准呢?我一直有这个疑问。
我个人的理解:当Viewgroup收到down事件,会先调用onInterceptTouchEvent,如果onInterceptTouchEvent返回true,孩子view不会有任何事件传入;但如果Viewgroup没有拦截down事件,让子View获得down事件,这样子view就有禁止父Viewgroup拦截的机会。还有重要的一点:只有子View对事件有响应(return true),父Viewgroup的onInterceptTouchEvent中才会出现 up和move等事件;否则Viewgroup的onInterceptTouchEvent中只有down事件。是否disallowIntercept是在onInterceptTouchEvent调用前判断的,因此可以在子View中控制父Viewgroup的对其他action是否拦截(通过requestDisallowInterceptTouchEvent)。(有什么不对请告知!)
自定义一个Viewgroup、ViewA和ViewB,重写dispatchTouchEvent ,onInterceptTouchEvent
onTouchEvent。
界面和默认的点击处理事件如下:
由日志可以看出:
1. dispatchTouchEvent 是由外往里传递,当然不是说Viewgroup包含多少子View就传递多少,在传递过程会做坐标判断。(只是传递给包含点击坐标的子view)。
这如下段代码摘抄至android2.2的Viewgroup dispatchTouchEvent 源码中。
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.
}
}
2. onTouchEvent是由里向外传递,若没有任何view响应,就交给activity处理。若其中有一个响应down事件,
该次触摸事件都会交予它处理(这是在没有拦截的基础上)。
3. onInterceptTouchEvent 是在dispatchTouchEvent 后调用的,默认返回值是false。
onTouchEvent、onInterceptTouchEvent 和dispatchTouchEvent 返回值网上有很多资料,
当onInterceptTouchEvent 返回true,Viewgroup就会调用自己的onTouchEvent。()
http://download.csdn.net/detail/fengyun703/9318719,这有实例代码,可以自己做实验。
推荐一篇很牛的文章,对事件传递讲解十分清楚。
xiaanming的博客http://blog.csdn.net/xiaanming/article/details/21696315#comments
在stackoverflow上找到这句话:
onInterceptTouchEvent only get called if the parent has a child view which returns "true"
from onTouchEvent. Once the child returns true, the parent now has a chance to
intercept that event.
自己做了实验:
如下图:
onInterceptTouchEvent 只在down事件处理中运行,只有Viewgroup的孩子响应该事件(
即return true),Viewgroup才有机会去拦截后续事件(即onInterceptTouchEvent 中才有move和up等事件)。
否则onInterceptTouchEvent中只有down事件。这也是正常思路:孩子有响应才去拦截。(最好能看源码,但看懂源码还有很困难,只能先从实验开始)
onInterceptTouchEvent 和requestDisallowInterceptTouchEvent到底哪个说了算。
onInterceptTouchEvent 在Viewgroup接到dwon事件后调用,若返回true,根本不会传递到子view。
但如果onInterceptTouchEvent 在down事件返回false,子view就有机会requestDisallowInterceptTouchEvent。
实验1:子View可以禁止父Viewgroup拦截事件。
我让Viewgroup在down事件时没有拦截,其他事件拦截。view的dispatchTouchEvent中调用requestDisallowInterceptTouchEvent,且view的ontouchEvent返回true。这样能实现子view禁止父viewgroup拦截触摸事件。
代码如下:
Viewgroup中:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i("test","MyViewGroup onInterceptTouchEvent "+ EventUtils.getMotion(ev));
//return super.onInterceptTouchEvent(ev);
int action = ev.getAction();
boolean result;
switch (action) {
case MotionEvent.ACTION_DOWN:
result = false;
break;
default:
result = true;
break;
}
return result;
}
ViewA中:
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i("test", "MyViewA dispatchTouchEvent "+EventUtils.getMotion(event));
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(event);
}
结果如下:
实验2:子View可以在不同action(更细微地)要求父Viewgroup是否拦截事件。
我让Viewgroup在down事件时没有拦截,其他事件拦截,而且onTouchEvent返回true。view的dispatchTouchEvent中调用requestDisallowInterceptTouchEvent,且view的ontouchEvent返回true。
Viewgroup代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//return super.onInterceptTouchEvent(ev);
int action = ev.getAction();
boolean result;
switch (action) {
case MotionEvent.ACTION_DOWN:
result = false;
break;
default:
result = true;
break;
}
Log.i("test","MyViewGroup onInterceptTouchEvent "+ EventUtils.getMotion(ev) +", Intercept = "+ result);
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i("test","MyViewGroup onTouchEvent "+ EventUtils.getMotion(event));
//return super.onTouchEvent(event);
return true;
}
ViewA代码:
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int action = event.getAction();
boolean disallow = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
disallow = true;
break;
case MotionEvent.ACTION_MOVE:
disallow = false;
break;
case MotionEvent.ACTION_UP:
disallow = false;
break;
default:
break;
}
getParent().requestDisallowInterceptTouchEvent(disallow);
Log.i("test",
"MyViewA dispatchTouchEvent " + EventUtils.getMotion(event)
+ ", disallow = " + disallow);
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i("test", "MyViewA onTouchEvent " + EventUtils.getMotion(event));
// return super.onTouchEvent(event);
return true;
}