天天看点

Android事件分发之ViewGroup篇 -- ViewGroup的dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent之间关系Android事件分发之ViewGroup篇(FatherViewGroup) – ViewGroup的dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent之间关系

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方法处理。