public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = onInterceptTouchEvent(ev);
if(consume){
consume = onTouchEvent(ev);
}else{
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
对于一个根ViewGroup来说,点击事件产生后,首先传递给它,这时它的dispatchTouchEvent就会被调用,如果这个ViewGroup的onInterceptTouchEvent返回true表示它要拦截当前事件,接着事件就交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用;如果返回false,则子元素的dispatchTouchEvent就会被调用。
public boolean dispatchTouchEvent(MotionEvent ev)
用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。
public boolean onInterceptTouchEvent(MotionEvent ev)
在上述方法内部调用,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。
public boolean onTouchEvent(MotionEvent ev)
在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次收到事件。
调用优先级:OnTouchListener > OnTouchEvent > OnClickListener
如果OnTouchListener中的OnTouch返回false,则OnTouchEvent被回调,否则不会。
事件产生后的传递顺序:Activity -> Window -> View
事件传递机制的结论:
1. 事件序列以down开始,经过一系列move,最终以up结束。//Activity#dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev){
if(ev.getAction() == MotionEvent.ACTION_DOWN){
onUserInteraction();
}
if(getWindow().superDispatchTouchEvent(ev)){//传递事件给decor view
return true;
}
return onTouchEvent(ev);//由Activity自己处理
}
三个因素影响ViewGroup调用onInterceptTouchEvent:当前为ACTION_DOWN事件或者mFirstTouchTarget!=null, 并且FLAG_DISALLOW_INTERCEPT未设置,当前事件由ViewGroup的子元素成功处理时,mFirstTouchTarget会被赋值并指向子元素;
子View处理事件的逻辑:在子View中找到没有正在播动画,并且点击事件坐标落在自View内的,调用它的dispatchTouchEvent,如果返回true则mFirstTouchTarget被赋值,如果返回false则点击事件被ViewGroup发送给下一个子元素或者直接由ViewGroup的super.dispatchTouchEvent处理。
View的dispatchTouchEvent:首先判断是否有OnTouchListener且是enabled状态,则调用onTouch如果返回true则onTouchEvent不会被调用;
View的onTouchEvent:disabled状态的view只要clickable和longClickable有一个为true,依然会消耗事件。
如果View设置了TouchDelegate,则会调用代理的onTouchEvent方法,如果设置了onClick,也会调用
滑动冲突:
1. 外部拦截法:重写父容器的onInterceptTouchEvent,在ACTION_MOVE中根据滑动方向判断是否需要由父容器拦截;
2. 内部拦截法:父容器不拦截任何事件,如果子元素需要此事件就消耗掉,否则设置为让父元素消耗;
重写子元素的dispatchTouchEvent方法,在ACTION_DOWN中设置requestDisallowInterceptTouchEvent(true),在ACTION_MOVE中根据需要修改requestDisallowInterceptTouchEvent;重写父容器的onInterceptTouchEvent,ACTION_DOWN返回false,其他返回true。