Android事件分发之ViewGroup篇(FatherViewGroup) – ViewGroup的dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent之间关系
前一篇分析了子View中dispatchTouchEvent、onTouchEvent、onTouch(OnTouchListener)之间关系,本篇将继续使用前一篇的demo来分析ViewGroup的dispatchTouchEvent、onTouchEvent,OnInterceptTouchEvent之间关系。demo的代码就不贴了,大家直接翻看前一篇。本篇只贴出修改部分的代码。(注意:无论是子View还是viewgroup的onTouchEvent的源代码都是直接使用的view的代码,因此ViewGroup的onTouchEvent方法于=与View的onTouchEvent方法的返回值是相同的,本篇就不再分析,有兴趣的自己去看源代码)。
既然ViewGroup比view中多了一个onInterceptTouchEvent方法,我们就从这个方法开始,从名字看是对TouchEvent的拦截,既父viewgroup对view进行事件拦截。
下面将代码恢复到原始状态(既demo刚建立时的代码,见前一篇),点击ChildView得到日志如下:
08-31 11:23:12.540 27552-27552/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_DOWN
08-31 11:23:12.541 27552-27552/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_DOWN
onInterceptTouchEvent_ACTION_DOWN
08-31 11:23:12.541 27552-27552/com.hzm.event E/ChildView: dispatchTouchEvent_ACTION_DOWN
08-31 11:23:12.542 27552-27552/com.hzm.event E/ChildView: onTouchEvent_ACTION_DOWN
08-31 11:23:12.543 27552-27552/com.hzm.event E/FatherViewGroup: onTouchEvent_ACTION_DOWN
08-31 11:23:12.545 27552-27552/com.hzm.event E/MainActivity: onTouchEvent_ACTION_DOWN
08-31 11:23:12.552 27552-27552/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
onTouchEvent_ACTION_MOVE
08-31 11:23:12.568 27552-27552/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
08-31 11:23:12.569 27552-27552/com.hzm.event E/MainActivity: onTouchEvent_ACTION_MOVE
08-31 11:23:12.585 27552-27552/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
onTouchEvent_ACTION_MOVE
08-31 11:23:12.593 27552-27552/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
08-31 11:23:12.594 27552-27552/com.hzm.event E/MainActivity: onTouchEvent_ACTION_MOVE
dispatchTouchEvent_ACTION_UP
08-31 11:23:12.595 27552-27552/com.hzm.event E/MainActivity: onTouchEvent_ACTION_UP
从日志可以看到ACTION_DOWN事件是先传递到了viewgroup的dispatchTouchEvent再传递到onInterceptTouchEvent,接着传递到ChildView的dispatchTouchEvent最后传递到ChildView的onTouchEvent方法中的。(大家可以测试ChildView的onTouchEvent方法对ACTION_MOVE,ACTION_UP事件进行消费,其传递流程也会和ACTION_DOWN一样,这里就不再测试了。)
onInterceptTouchEvent的返回值对事件传递的影响。
修改Viewgroup的onInterceptTouchEvent代码直接返回true;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_UP");
break;
}
super.onInterceptTouchEvent(ev);
return true;
}
点击ChildView的日志如下:
08-31 11:36:06.914 28394-28394/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_DOWN
08-31 11:36:06.914 28394-28394/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_DOWN
08-31 11:36:06.915 28394-28394/com.hzm.event E/FatherViewGroup: onInterceptTouchEvent_ACTION_DOWN
onTouchEvent_ACTION_DOWN
08-31 11:36:06.917 28394-28394/com.hzm.event E/MainActivity: onTouchEvent_ACTION_DOWN
08-31 11:36:06.938 28394-28394/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
onTouchEvent_ACTION_MOVE
08-31 11:36:06.954 28394-28394/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
onTouchEvent_ACTION_MOVE
08-31 11:36:06.959 28394-28394/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_UP
08-31 11:36:06.959 28394-28394/com.hzm.event E/MainActivity: onTouchEvent_ACTION_UP
小结:从日志可以看到ACTION_DOWN事件,从Activity的dispatchTouchEvent传递到ViewGroup的dispatchTouchEvent,再到onInterceptTouchEvent最后到ViewGroup的onTouchEvent,事件不再分发给ChildView了。也就是说onInterceptTouchEvent的返回值决定了事件是否继续向下传递。
下面我们测试ViewGroup前把ChildView的onTouchEvent设置返回true,目的是让ChildView消耗事件。既子view处理所有点击或触摸事件。(前面文章详细分析过)这样以便排除干扰,测试事件能否传递到ChildView。
ChildView的代码修改如下:
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onTouchEvent"+"_ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onTouchEvent"+"_ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onTouchEvent"+"_ACTION_UP");
break;
}
// super.onTouchEvent(ev);
return true;
}
修改Viewgroup的onInterceptTouchEvent和onTouchEvent方法:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean flag=false;
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
flag=true;
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_DOWN:"+flag);
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_MOVE:"+flag);
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_UP:"+flag);
break;
}
super.onInterceptTouchEvent(ev);
return flag;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onTouchEvent"+"_ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onTouchEvent"+"_ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onTouchEvent"+"_ACTION_UP");
break;
}
super.onTouchEvent(ev);
return true;//返回true,以便事件在此被消费
}
点击ChildView的日志如下:
08-31 14:35:14.907 17306-17306/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_DOWN
08-31 14:35:14.908 17306-17306/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_DOWN
onInterceptTouchEvent_ACTION_DOWN:true
onTouchEvent_ACTION_DOWN
08-31 14:35:14.923 17306-17306/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
08-31 14:35:14.923 17306-17306/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_MOVE
08-31 14:35:14.924 17306-17306/com.hzm.event E/FatherViewGroup: onTouchEvent_ACTION_MOVE
08-31 14:35:14.931 17306-17306/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
08-31 14:35:14.931 17306-17306/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_MOVE
onTouchEvent_ACTION_MOVE
08-31 14:35:14.932 17306-17306/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_UP
08-31 14:35:14.932 17306-17306/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_UP
onTouchEvent_ACTION_UP
再次修改Viewgroup的onInterceptTouchEvent和onTouchEvent方法:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean flag=false;
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_DOWN:"+flag);
break;
case MotionEvent.ACTION_MOVE:
flag=true;
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_MOVE:"+flag);
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_UP:"+flag);
break;
}
super.onInterceptTouchEvent(ev);
return flag;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onTouchEvent"+"_ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onTouchEvent"+"_ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onTouchEvent"+"_ACTION_UP");
break;
}
super.onTouchEvent(ev);
return true;//返回true,以便事件在此被消费
}
点击ChildView的日志如下:
08-31 14:32:12.392 16833-16833/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_DOWN
08-31 14:32:12.393 16833-16833/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_DOWN
onInterceptTouchEvent_ACTION_DOWN:false
08-31 14:32:12.393 16833-16833/com.hzm.event E/ChildView: dispatchTouchEvent_ACTION_DOWN
08-31 14:32:12.394 16833-16833/com.hzm.event E/ChildView: onTouchEvent_ACTION_DOWN
08-31 14:32:12.412 16833-16833/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
08-31 14:32:12.412 16833-16833/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_MOVE
onInterceptTouchEvent_ACTION_MOVE:true
08-31 14:32:12.428 16833-16833/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
08-31 14:32:12.429 16833-16833/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_MOVE
onTouchEvent_ACTION_MOVE
08-31 14:32:12.433 16833-16833/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
08-31 14:32:12.434 16833-16833/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_MOVE
onTouchEvent_ACTION_MOVE
08-31 14:32:12.435 16833-16833/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_UP
08-31 14:32:12.435 16833-16833/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_UP
onTouchEvent_ACTION_UP
小结:每次事件通过viewgroup的dispatchTouchEvent方法后,会转交给onInterceptTouchEvent方法,如果onInterceptTouchEvent方法返回true后,则以后的所有事件都拦截,并将事件交给viewgroup自己的onTouchEvent处理,如果返回false则交给子view的dispatchTouchEvent方法处理。(注意:一旦onInterceptTouchEvent方法返回true,该系列以后所有事件也都不再传递到onInterceptTouchEvent方法,既一旦拦截后续所有事件都会被拦截)。
dispatchTouchEvent方法的返回值对事件传递的影响:
还原ViewGroup中onInterceptTouchEvent方法的代码,修改dispatchTouchEvent方法的代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onInterceptTouchEvent"+"_ACTION_UP:");
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"dispatchTouchEvent"+"_ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"dispatchTouchEvent"+"_ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"dispatchTouchEvent"+"_ACTION_UP");
break;
}
super.dispatchTouchEvent(ev);
return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onTouchEvent"+"_ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onTouchEvent"+"_ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onTouchEvent"+"_ACTION_UP");
break;
}
return super.onTouchEvent(ev);
}
修改ChildView的代码如下:
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onTouchEvent"+"_ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onTouchEvent"+"_ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onTouchEvent"+"_ACTION_UP");
break;
}
// super.onTouchEvent(ev);
return true;
}
点击ChildView的日志如下:
09-01 10:02:53.514 4098-4098/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_DOWN
09-01 10:02:53.515 4098-4098/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_DOWN
09-01 10:02:53.516 4098-4098/com.hzm.event E/FatherViewGroup: onInterceptTouchEvent_ACTION_DOWN
09-01 10:02:53.516 4098-4098/com.hzm.event E/ChildView: dispatchTouchEvent_ACTION_DOWN
onTouchEvent_ACTION_DOWN
09-01 10:02:53.518 4098-4098/com.hzm.event E/MainActivity: onTouchEvent_ACTION_DOWN
09-01 10:02:53.528 4098-4098/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
onTouchEvent_ACTION_MOVE
09-01 10:02:53.544 4098-4098/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
09-01 10:02:53.545 4098-4098/com.hzm.event E/MainActivity: onTouchEvent_ACTION_MOVE
09-01 10:02:53.549 4098-4098/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
09-01 10:02:53.550 4098-4098/com.hzm.event E/MainActivity: onTouchEvent_ACTION_MOVE
dispatchTouchEvent_ACTION_UP
09-01 10:02:53.551 4098-4098/com.hzm.event E/MainActivity: onTouchEvent_ACTION_UP
小结:可以看到ChildView的onTouchEvent即使返回true的情况下,他也无法得到事件的处理权限。既最后的事件交回给了Activity的onTouchEvent来处理。(既所有的view都未消费事件,事件交回给activity处理)。
再次修改ViewGroup中dispatchTouchEvent方法的代码如下(其他代码保持不变):
09-01 10:16:18.882 5696-5696/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_DOWN
09-01 10:16:18.883 5696-5696/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_DOWN:true
onInterceptTouchEvent_ACTION_DOWN
09-01 10:16:18.883 5696-5696/com.hzm.event E/ChildView: dispatchTouchEvent_ACTION_DOWN
09-01 10:16:18.884 5696-5696/com.hzm.event E/ChildView: onTouchEvent_ACTION_DOWN
09-01 10:16:18.911 5696-5696/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
09-01 10:16:18.912 5696-5696/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_MOVE:false
onInterceptTouchEvent_ACTION_MOVE
09-01 10:16:18.912 5696-5696/com.hzm.event E/ChildView: dispatchTouchEvent_ACTION_MOVE
onTouchEvent_ACTION_MOVE
09-01 10:16:18.913 5696-5696/com.hzm.event E/MainActivity: onTouchEvent_ACTION_MOVE
09-01 10:16:18.928 5696-5696/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_MOVE
09-01 10:16:18.929 5696-5696/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_MOVE:false
onInterceptTouchEvent_ACTION_MOVE
09-01 10:16:18.929 5696-5696/com.hzm.event E/ChildView: dispatchTouchEvent_ACTION_MOVE
onTouchEvent_ACTION_MOVE
09-01 10:16:18.929 5696-5696/com.hzm.event E/MainActivity: onTouchEvent_ACTION_MOVE
09-01 10:16:18.930 5696-5696/com.hzm.event E/MainActivity: dispatchTouchEvent_ACTION_UP
09-01 10:16:18.930 5696-5696/com.hzm.event E/FatherViewGroup: dispatchTouchEvent_ACTION_UP:false
onInterceptTouchEvent_ACTION_UP:
09-01 10:16:18.930 5696-5696/com.hzm.event E/ChildView: dispatchTouchEvent_ACTION_UP
onTouchEvent_ACTION_UP
09-01 10:16:18.930 5696-5696/com.hzm.event E/MainActivity: onTouchEvent_ACTION_UP
小结:可以看到即使ChildView的onTouchEvent对所有事件都返回true(既消费了所有事件),只要ViewGroup的dispatchTouchEvent对该事件的返回值不为true(如ACTION_MOVE,ACTION_UP),那么事件的最终都会交回给Activity的onTouchEvent来处理。既表示子View和ViewGroup没有消费该事件(ACTION_MOVE,ACTION_UP),也就是说ViewGroup中dispatchTouchEvent方法的返回值表明了该事件是否被消费,如果没有消费(返回false),就交换给activity的onTouchEvent来消费。
根据上面的这些分析,可以得到下面的伪代码(有兴趣的读者可以看下ViewGroup的dispatchTouchEvent方法的源代码,代码较长就不贴源码了):
ViewGroup:
public boolean dispatchTouchEvent(MotionEvent event) {
if(onInterceptTouchEvent(event)){//是否拦截
return onTouchEvent(event);
}
//没有拦截
if(child==null){
//没有子控件
return onTouchEvent(event);
}else{
//执行子控件的dispatchTouchEvent
boolean consume= child.dispatchTouchEvent(event);
if(!consume){//子控件没有消费事件,执行当前view的onTouchEvent
return onTouchEvent(event);
}else{
return false;
}
}
}
总结
1,事件是先传递到ViewGroup后传递到View的。
2,ViewGroup中通过onInterceptTouchEvent方法返回值对事件进行拦截,如果onInterceptTouchEvent方法返回true后,则以后的所有事件都拦截,并将事件交给viewgroup自己的onTouchEvent处理,如果返回false则交给子view的dispatchTouchEvent方法处理。