Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android事件分發流程

android事件分發流程

編輯:關於Android編程

1.描述

說到android事件的分發機制,真的是感覺既熟悉又陌生,因為每次需要用到的時候查看相關的源碼,總能找到一些所以然來,但是要根據自己理解從頭到尾說一遍,卻一點都說不上。總結原因吧,感覺是自己不善於總結,過目就忘,並沒有把心思放在上面,自然也就沒有一點概念咯~~所以在這裡主要是把自己理解的一些東西記錄下來,不涉及源代碼。

 

好吧,接下來簡單說說android事件分發流程吧,說到事件分發,首先應該想到的是兩個類,View和ViewGroup,ViewGroup是繼承自View實現的,ViewGroup是控件容器,主要作用是包含具體控件,可以把ViewGroup想象成為一個盒子,裡面按規則包含各種各樣的控件,而包含控件的ViewGroup又可以把它當一個基本的控件單元,包含在另外一個ViewGroup中。實際上一個觸摸事件是由上層的父類傳遞進來的,而最基本的ViewGroup的觸摸事件則是由Activity傳入的,Activity也有dispathTouchEvent()和onTouchEvent()方法,Activity接收到觸摸事件時,會調用內部ViewGroup的dispathTouchEvent()方法。

 

事件分發流程和View相關的方法主要有兩個:dispathTouchEvent()和onTouchEvent(), dispathTouchEvent()是分發事件的意思,onTouchEvent()才是真正處理事件的地方,實際上在View的dispathTouchEvent()方法中是通過調用onTouchEvent()處理事件的。在這裡和觸摸相關的還有一個onTouch方法,這個是使用控件的setOnTouchListene()設置的,它是滿足一定條件時在onTouchEvent()方法之前調用的。

 

ViewGroup相關的方法除了dispathTouchEvent()和onTouchEvent(),還有一個onInterceptTouchEvent()方法,它表示的是攔截事件的意思,默認返回值是false,表示不攔截事件。另外一個就是onTouch方法了,這個也是使用控件的setOnTouchListene()設置的。

 

2.測試界面:

\

 

3.測試代碼:

Activity_main.xml

 



 
   
 
   
        
       
        
   
 
   
 

 

 

MainActivity.java

 

public class MainActivity extends Activity {
 
    private MyViewGroup mvgV2 = null;
    private MyView mvV2 = null;
 
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(Config.show_onTouch) {
            mvgV2 = (MyViewGroup) findViewById(R.id.mvg_v2);
            mvV2 = (MyView) findViewById(R.id.mv_v2);
            mvgV2.setOnTouchListener(new MyTouchListener());
            mvgV2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
 
                }
            });
            mvV2.setOnTouchListener(new MyTouchListener());
            mvV2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
 
                }
            });
        }
    }
 
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(Config.show_main) {
            MyLog.log("MainActivity" + ": " + "dispatchTouchEvent");
        }
        //       return super.dispatchTouchEvent(ev);
        if(Config.detail) {
            switch(ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log("MainActivity" + ": " + "dispatchTouchEvent ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log("MainActivity" + ": " + "dispatchTouchEvent ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log("MainActivity" + ": " + "dispatchTouchEvent ACTION_UP");
                    break;
            }
        }
        boolean flag = super.dispatchTouchEvent(ev);
        if(Config.show_default) {
            MyLog.log("MainActivity dispatchTouchEventreturn" + ": " + flag);
        }
 
        return flag;
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(Config.show_main) {
            MyLog.log("MainActivity" + ": " + "onTouchEvent");
        }
        //       return super.onTouchEvent(event);
        if(Config.detail) {
            switch(event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log("MainActivity" + ": " + "onTouchEvent ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log("MainActivity" + ": " + "onTouchEvent ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log("MainActivity" + ": " + "onTouchEvent ACTION_UP");
                    break;
            }
        }
        boolean flag = super.onTouchEvent(event);
        if(Config.show_default) {
            MyLog.log("MainActivity onTouchEventreturn" + ": " + flag);
        }
 
        return flag;
    }
 
    private class MyTouchListenerimplements View.OnTouchListener {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if(v.getId() == R.id.mvg_v2) {
                if(Config.show_main) {
                    MyLog.log(v.getTag().toString()+ ": " + "onTouch");
                }
                if(Config.detail) {
                    switch(event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_DOWN");
                            break;
                        case MotionEvent.ACTION_MOVE:
                            MyLog.log(v.getTag().toString()+ ": " + "onTouchACTION_MOVE");
                            break;
                        case MotionEvent.ACTION_UP:
                            MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_UP");
                            break;
                    }
                }
                return false;
            } else if(v.getId() == R.id.mv_v2) {
                if(Config.show_main) {
                    MyLog.log(v.getTag().toString()+ ": " + "onTouch");
                }
                if(Config.detail) {
                    switch(event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_DOWN");
                            break;
                        case MotionEvent.ACTION_MOVE:
                            MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_MOVE");
                            break;
                        case MotionEvent.ACTION_UP:
                            MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_UP");
                            break;
                    }
                }
                return false;
            }
            return false;
        }
    }
 
 
}

 

MyView.java

 

public class MyView extends TextView {
 
    public MyView(Contextcontext) {
        super(context);
    }
 
    public MyView(Contextcontext, AttributeSet attrs) {
        super(context, attrs);
    }
 
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(Config.show_main) {
            MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent");
        }
        if(Config.detail) {
            switch(ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP");
                    break;
            }
        }
        boolean flag = super.dispatchTouchEvent(ev);
        if(Config.show_default) {
            MyLog.log(getTag().toString()+ "dispatchTouchEvent return: " + flag);
        }
 
        return flag;
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(Config.show_main) {
            MyLog.log(getTag().toString()+ ": " + "onTouchEvent");
        }
//       return super.onTouchEvent(event);
        if(Config.detail) {
            switch(event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_UP");
                    break;
            }
        }
        boolean flag = super.onTouchEvent(event);
        if(Config.show_default) {
            MyLog.log(getTag().toString()+ "onTouchEvent return: " + flag);
        }
 
        return flag;
    }
 
 
}
 

 

MyViewGroup.java

 

public class MyViewGroup extends LinearLayout {
 
    public MyViewGroup(Contextcontext, AttributeSet attrs) {
        super(context, attrs);
    }
 
    public MyViewGroup(Contextcontext) {
        super(context);
    }
 
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(Config.show_main) {
            MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent");
        }
        if(Config.detail) {
            switch(ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP");
                    break;
            }
        }
        boolean flag = super.dispatchTouchEvent(ev);
 
        if(Config.show_default) {
            MyLog.log(getTag().toString()+ "dispatchTouchEvent return: " + flag);
        }
 
        return flag;
//        return false;
    }
 
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(Config.show_main) {
            MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent");
        }
//        return true;
        if(Config.detail) {
            switch(ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_UP");
                    break;
            }
        }
        boolean flag = super.onInterceptTouchEvent(ev);
        if(Config.show_default) {
            MyLog.log(getTag().toString()+ " onInterceptTouchEventreturn: " + flag);
        }
 
        return flag;
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(Config.show_main) {
            MyLog.log(getTag().toString()+ ": " + "onTouchEvent");
        }
        if(Config.detail) {
            switch(event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_UP");
                    break;
            }
        }
        boolean flag = super.onTouchEvent(event);
        if(Config.show_default) {
            MyLog.log(getTag().toString()+ "onTouchEvent return: " + flag);
        }
        return flag;
    }
 
}

 

4.測試例子:

4.1. 測試1

4.1.1.測試條件:

測試默認情況下時間分發流程,把Config.java的修改如下:

 

public staticfinal boolean show_onTouch= false; // 顯示onTouch方法內容
public staticfinal boolean detail = false; // 顯示詳細的action內容
public staticfinal boolean show_main= true; // 顯示主要內容
public staticfinal boolean show_default= true; // 顯示默認返回值

 

1. 保持返回值為默認值,分別設置如下:

MainActivity:

dispathTouchEvent():super.dispathTouchEvent()

onTouchEvent():super. onTouchEvent()

MyViewGroup:

dispathTouchEvent():super.dispathTouchEvent()

onInterceptTouchEvent():super.onInterceptTouchEvent()

onTouchEvent():super.onTouchEvent()

MyView:

dispathTouchEvent():super.dispathTouchEvent()

onTouchEvent():super.onTouchEvent()

其他地方不做修改,運行程序。

4.1.2.輸出結果:

\

 

4.1.3.流程分析:

見下圖:

\

 

由此可見,觸摸事件最先傳遞到MainActivity,使用dispathTouchEvent()進行分發,內部調用了viewgroup1的dispathTouchEvent(),之後調用onInterceptTouchEvent()判斷是否攔截事件,這裡返回false,緊接著調用viewgroup2的dispathTouchEvent(),通過遍歷viewgroup2的子控件,最後找到了view2控件,調用view2的dispatchTouchEvent()方法,內部調用了onTouchEvent()方法,所有的方法都返回了false,所以又繼續返回父控件,最後到MainActivity的onTouchEvent()。為什麼是這樣的流程需要看下源碼,裡面可以很清晰了解具體的流程,這裡只想簡單說明事件分發的流程。

 

4.1.4.結論

1.默認情況下

dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()返回值都是false,表示不消費touch事件。

2.view的事件分發過程:dispatchTouchEvent()->onTouchEvent()

3.viewgroup事件分發過程:

dispatchTouchEvent()->onInterceptTouchEvent()->onTouchEvent()

上述結果都是在各個處理流程返回默認值的時候成立,也就是沒有控件消費事件的時候。

 

4.2. 測試2

4.2.1.測試條件

在activity_main.xml中對viewgroup2和view2設置,加入如下代碼:

 

android:clickable="true"

 

把Config.java的修改如下:

 

public staticfinal boolean show_onTouch= true; // 顯示onTouch方法內容
public staticfinal boolean detail = false; // 顯示詳細的action內容
public staticfinal boolean show_main= true; // 顯示主要內容
public staticfinal boolean show_default= false; // 顯示默認返回值

 

各方法返回值:

MainActivity:

dispathTouchEvent():super.dispathTouchEvent()

onTouchEvent():super. onTouchEvent()

MyViewGroup:

dispathTouchEvent(): super.dispathTouchEvent()

onInterceptTouchEvent():super.onInterceptTouchEvent ()

onTouch(): false

onTouchEvent():super.onTouchEvent()

MyView:

dispathTouchEvent():super.dispathTouchEvent()

onInterceptTouchEvent():super.onInterceptTouchEvent()

onTouch():false

onTouchEvent():super.onTouchEvent()

 

4.2.2.輸出結果

\

 

4.2.3.流程分析

由圖可見,view2的onTouch已經觸發了,它是在dispatchTouchEvent()和onTouchEvent()之間調用的,調用順序是:

dispatchTouchEvent()->onTouch()->onTouchEvent()。

另外,由於設置了view2的clickable屬性,它的onTouchEvent()是返回true的。所以viewgroup2的onTouch()並沒有觸發,需要把MyView的onTouchEvent()返回值設置成false才會觸發。這裡就不貼測試結果了,viewgroup2的調用順序是這樣的:

dispatchTouchEvent()->onInterceptTouchEvent()->onTouch()->onTouchEvent(),結合View和ViewGroup的例子說明,onTouch()是在onTouchEvent()之前調用的。

因為View的onTouchEvent()返回true,表示已經消費了改事件,所以事件不會向上投遞了,後面的ViewGroup的onTouchEvent()也不會調用。

4.2.4.結論

1.View調用順序是:dispatchTouchEvent()->onTouch()->onTouchEvent()。

2.ViewGroup調用順序:

dispatchTouchEvent()->onInterceptTouchEvent()->onTouch()->onTouchEvent()

3.如果設置了View的clickable屬性,那麼onTouchEvent()返回true,代表消費該事件,後續將不會繼續投遞該事件。

 

4.3. 測試3

4.3.1.測試條件

單純把MyView的onTouchEvent()的返回值設置成false,其他條件和測試2保持不變,代碼如下:

 

   @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(Config.show_main) {
            MyLog.log(getTag().toString()+ ": " + "onTouchEvent");
        }
//       return super.onTouchEvent(event);
        if(Config.detail) {
            switch(event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_UP");
                    break;
            }
        }
        boolean flag = super.onTouchEvent(event);
        if(Config.show_default) {
            MyLog.log(getTag().toString()+ "onTouchEvent return: " + flag);
        }
 
        return false;
    }

 

4.3.2.輸出結果

\

 

4.3.3.流程分析

可以看到,當view2的onTouchEvent()返回false時,表示事件沒有被消費,所以繼續向上投遞,viewgroup2的onTouch()和onTouchEvent()都會觸發。因為viewgroup2設置了clickable屬性,所以它的onTouchEvent()是返回true的,所以事件就不會繼續向上投遞了,如果把ViewGroup的onTouchEvent()返回值手動改成false,那麼viewgroup1的onTouchEvent()還是會觸發的。

4.3.4.結論

1.當一個View設置了clickable為true屬性之後,他的onTouchEvent()會返回true,表示消費了touch事件。

 

 

4.4. 測試4

4.4.1.測試條件

設置返回值:

MainActivity:

dispathTouchEvent():super.dispathTouchEvent()

onTouchEvent():super. onTouchEvent()

MyViewGroup:

dispathTouchEvent():super.dispathTouchEvent()

onInterceptTouchEvent():super.onInterceptTouchEvent()

onTouch():false

onTouchEvent():super.onTouchEvent()

MyView:

dispathTouchEvent():super.dispathTouchEvent()

onInterceptTouchEvent():super.onInterceptTouchEvent()

onTouch():true

onTouchEvent():super.onTouchEvent()

其他條件和測試3一樣

 

4.4.2.輸出結果

\

 

4.4.3.流程分析

可以看到把view2的onTouch返回true,則接下來的onTouchEvent()也不會調用,因為這樣設置代表view2已經處理了事件,那麼之後onTouchEvent()也不會執行了。

4.4.4.結論

1.當onTouch ()返回true之後,後續的onTouchEvent()不會觸發

 

4.5. 測試5

4.5.1.測試條件

把Config.java的修改如下:

 

public staticfinal boolean show_onTouch= false; // 顯示onTouch方法內容
public staticfinal boolean detail = true; // 顯示詳細的action內容
public staticfinal boolean show_main= false; // 顯示主要內容
public staticfinal boolean show_default= false; // 顯示默認返回值

 

設置如下返回值

MainActivity:

dispathTouchEvent(): super.dispathTouchEvent()

onTouchEvent(): super.onTouchEvent()

MyViewGroup:

dispathTouchEvent():true

onInterceptTouchEvent():super.onInterceptTouchEvent()

onTouch(): false

onTouchEvent(): super.onTouchEvent()

MyView:

dispathTouchEvent(): super.dispathTouchEvent()

onInterceptTouchEvent():super.onInterceptTouchEvent()

onTouch(): false

onTouchEvent(): super.onTouchEvent()

ViewGroup的dispathTouchEvent()代碼如下:

 

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(Config.show_main) {
            MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent");
        }
        if(Config.detail) {
            switch(ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP");
                    break;
            }
        }
//        boolean flag =super.dispatchTouchEvent(ev);
//
//        if(Config.show_default) {
//            MyLog.log(getTag().toString() +" dispatchTouchEvent return: " + flag);
//        }
//
//        return flag;
        return true;
    }

 

4.5.2.輸出結果

\

 

4.5.3.流程分析

當viewgroup1的dispathcTouchEvent()返回true之後,代表dispathcTouchEvent()消費了改事件,那麼事件不會繼續往下投遞,所以後面的viewgroup2和view2都不會接受到此事件,另外一個對於viewgroup1來說,接下來的onTouchEvent()也不會執行,因為事件已經被消費掉了。

4.5.4.結論

1.ViewGroup的dispathcTouchEvent()返回true,代表關注該事件,後續的touch事件由它處理。

 

4.6. 測試6

4.6.1.測試條件

在測試5的基礎上修改

MainActivity:

dispathTouchEvent(): super.dispathTouchEvent()

onTouchEvent(): super.onTouchEvent()

MyViewGroup:

dispathTouchEvent():false

onInterceptTouchEvent():super.onInterceptTouchEvent()

onTouch(): super.onTouch()

onTouchEvent(): super.onTouchEvent()

MyView:

dispathTouchEvent(): super.dispathTouchEvent()

onInterceptTouchEvent():super.onInterceptTouchEvent()

onTouch(): super.onTouch()

onTouchEvent(): super.onTouchEvent()

4.6.2.輸出結果

\

 

4.6.3.流程分析

當把ViewGroup的dispathTouchEvent()設置為false,表示當前的ViewGroup並不關心該事件,那麼下次事件的ACTION_MOVE和ACTION_UP它也不會處理,直接交給了MainActivity進行處理,但是當ACTION_UP觸發之後,表示一個完整事件已經完成,那麼當ACTION_DOWN再次觸發的時候,事件還是會投遞到ViewGroup,當ViewGroup的dispathTouchEvent()返回false,表示不關系此事件,那麼後續的ACTION_MOVE和ACTION_UP會直接分發給MainActivity進行處理。

4.6.4.結論

1. 一個完成的觸摸事件一般包括ACTION_DOWN,ACTION_MOVE,ACTION_UP等,ACTION_MOVE,ACTION_UP在一個觸摸事件中不一定會出現。

2. ViewGroup的dispathTouchEvent()返回false,之後的action不會繼續投遞到該ViewGroup進行處理。這些action包括ACTION_MOVE,ACTION_UP等,直到下一次ACTION_DOWN事件出現

 

4.7. 測試7

4.7.1.測試條件

設置如下返回值

MainActivity:

dispathTouchEvent(): super.dispathTouchEvent()

onTouchEvent(): super.onTouchEvent()

MyViewGroup:

dispathTouchEvent():uper.dispathTouchEvent()

onInterceptTouchEvent():false

onTouch(): super.onTouch()

onTouchEvent(): super.onTouchEvent()

MyView:

dispathTouchEvent(): super.dispathTouchEvent()

onInterceptTouchEvent():super.onInterceptTouchEvent()

onTouch(): super.onTouch()

onTouchEvent(): super.onTouchEvent()

修改ViewGroup的代碼如下:

 

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(Config.show_main) {
            MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent");
        }
        if(Config.detail) {
            switch(ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP");
                    break;
            }
        }
        boolean flag = super.dispatchTouchEvent(ev);
 
        if(Config.show_default) {
            MyLog.log(getTag().toString()+ "dispatchTouchEvent return: " + flag);
        }
 
        return flag;
//        return false;
    }
 
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(Config.show_main) {
            MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent");
        }
//        return true;
        if(Config.detail) {
            switch(ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_MOVE");
                    return true;
//                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_UP");
                    break;
            }
        }
//        boolean flag =super.onInterceptTouchEvent(ev);
//        if(Config.show_default) {
//            MyLog.log(getTag().toString() +" onInterceptTouchEvent return: " + flag);
//        }
 
        return false;
    }


 

4.7.2.輸出結果

\

4.7.3.流程分析

流程和測試1保持默認值是一樣的。

 

4.8. 測試8

4.8.1.測試條件

在測試7的條件下設置如下:

MainActivity:

dispathTouchEvent(): super.dispathTouchEvent()

onTouchEvent(): super.onTouchEvent()

MyViewGroup:

dispathTouchEvent():uper.dispathTouchEvent()

onInterceptTouchEvent():true

onTouch(): super.onTouch()

onTouchEvent():true

MyView:

dispathTouchEvent(): super.dispathTouchEvent()

onInterceptTouchEvent():super.onInterceptTouchEvent()

onTouch(): super.onTouch()

onTouchEvent(): super.onTouchEvent()

4.8.2.輸出結果

\

4.8.3.流程分析

由於viewgroup1的onInterceptTouchEvent()返回true,表示截取該事件,所以事件不會繼續向下投遞,直接有viewgroup1的onTouchEvent()進行處理,如果viewgroup1的

onTouchEvent()返回true,那麼下次觸摸事件還會繼續進入到viewgroup1,由viewgroup1進行處理。

4.8.4.結論

1. onInterceptTouchEvent()方法表示是否事件,默認返回值是false,表示不攔截事件。

2. onInterceptTouchEvent()返回true表示攔截事件,事件不會繼續向下投遞,直接交給onTouchEvent()處理。

 

5.關於ViewGroup的事件攔截

ViewGroup的onInterceptTouchEvent()返回true之後,表示事件被攔截,那麼事件將不會繼續分發下去,有沒有辦法設置父類不攔截事件呢,ViewGroup給我們提供了一個方法:

requestDisallowInterceptTouchEvent()方法,如果我們想父類不攔截touch事件,那麼只需要設置成requestDisallowInterceptTouchEvent(true)即可。

requestDisallowInterceptTouchEvent()這個方法不能在ViewGrop的dispathTouchEvent()裡面設置,因為ViewGroup在ACTION_DOWN時會把FLAG_DISALLOW_INTERCEPT設置為false,相當於之前設置的requestDisallowInterceptTouchEvent()並沒有起作用,這個時候如果事件被攔截,那麼事件將不會繼續分發到子View,事件還是會被攔截,相當於requestDisallowInterceptTouchEvent()設置並沒有起作用。

5.1. 測試1

5.1.1.測試條件

基於上述測試8代碼修改,把MyViewGroup修改為:

 

  @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(Config.show_main) {
            MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent");
        }
//        return true;
        if(Config.detail) {
            switch(ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_MOVE");
                    return true;
//                    break;
                case MotionEvent.ACTION_UP:
                    MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_UP");
                    break;
            }
        }
//        boolean flag =super.onInterceptTouchEvent(ev);
//        if(Config.show_default) {
//            MyLog.log(getTag().toString() +" onInterceptTouchEvent return: " + flag);
//        }
 
        return false;
    }


 

這裡只是在ACTION_MOVE截取了,子類不會接收到這個touch事件。

5.1.2.輸出結果

\

5.1.3.流程分析

由於在ViewGroup代碼中設置了onInterceptTouchEvent()的ACTION_MOVE返回true,所以ACTION_MOVE後面的action子View都不會接受到,本次ACTION_MOVE就直接由ViewGroup的onTouchEvent()進行消費了,我們在onTouchEvent()返回true,那麼這個touch事件的其他action以後都由這個ViewGroup進行處理。onInterceptTouchEvent()在之後的action都不會觸發。

5.1.4.結論

onInterceptTouchEvent()返回true會截取該事件,由截取該事件的View進行處理,而且onInterceptTouchEvent()在下次ACTION_DOWN來臨之前都不會重復調用。

 

5.2. 測試2

5.2.1.測試條件

在上述測試的基礎上修改MyView代碼:

 

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if(Config.show_main) {
        MyLog.log(getTag().toString() + ": " + "dispatchTouchEvent");
    }
    if(Config.detail) {
        switch(ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN");
               getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP");
                break;
        }
    }
    boolean flag = super.dispatchTouchEvent(ev);
    if(Config.show_default) {
        MyLog.log(getTag().toString() + " dispatchTouchEvent return: "+ flag);
    }
 
    return flag;
}


 

5.2.2.輸出結果

 

5.2.3.流程分析

和上述測試結果對比可以指,ViewGroup對ACTION_MOVE的處理還是會分發到子View,這是由於子View調用了getParent().requestDisallowInterceptTouchEvent(true);告訴父類不要截取事件,所以之後的action還是會分發到子View上。

5.2.4.結論

requestDisallowInterceptTouchEvent(true)可以讓父控件不截取事件。

 

6.補充

onTouch()觸發條件

添加onTouch()方法,在activity_main.xml中對viewgroup2和view2設置,加入如下代碼:

 

android:clickable="true"


 

但是我加入了之後還是不會觸發onTouch(),查看了源碼之後才發現新版本和舊版本原來存在差異,在新版本中代碼是這樣的:

 

ListenerInfoli = mListenerInfo;
if (li != null && li.mOnTouchListener != null
        && (mViewFlags & ENABLED_MASK) == ENABLED
        && li.mOnTouchListener.onTouch(this, event)) {
    result = true;
}


 

而這個ListenerInfo的初始化可以在setOnClickListener()設置,當然其他地方會有設置。

 

public void setOnClickListener(@Nullable OnClickListener l) {
    if (!isClickable()) {
        setClickable(true);
    }
    getListenerInfo().mOnClickListener = l;
}


 

所以可以給這個控件設置一個點擊監聽器,就可以出發onTouch()方法了。

在MainActivity中加入如下代碼:

 

mvgV2 = (MyViewGroup) findViewById(R.id.mvg_v2);
mvV2 = (MyView) findViewById(R.id.mv_v2);
mvgV2.setOnTouchListener(new MyTouchListener());
mvgV2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    }
});
mvV2.setOnTouchListener(new MyTouchListener());
mvV2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    }
});

 

7.總結

1. 默認情況下dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()返回值都是false,表示不消費touch事件。

2.view的事件分發過程:dispatchTouchEvent()->onTouchEvent()

3.viewgroup事件分發過程:

dispatchTouchEvent()->onInterceptTouchEvent()->onTouchEvent()

4.對於View和ViewGroup來說,onTouch()的調用在onTouchEvent()之前。

5. 如果設置了View的clickable屬性,那麼onTouchEvent()返回true

6.返回值true表示關注此事件,如果一個View返回true,後續的事件都會分發給該View處理。

7.如果一個View返回false,表示不關注此事件,那麼該事件後續的action比如ACTION_MOVE,ACTION_UP都不會分發給該View。

8.onInterceptTouchEvent()用來判斷是否截取事件,默認不截取,如果返回true,表示截取該事件,那麼之後的touch事件直接交由該View的onTouchEvent()進行處理。

9.可以通過requestDisallowInterceptTouchEvent()設置父類是否截取事件,傳入true表示不截取事件。

10.requestDisallowInterceptTouchEvent()不能在ViewGroup的dispathTouchEvent中設置,因為ACTION_DOWN到來是會重新把FLAG_DISALLOW_INTERCEP設置成false。

最後附上一張流程圖:

\

 

測試代碼

 

 

 

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved