天天看點

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