1. 程式人生 > >Android-onInterceptTouchEvent()和onTouchEvent()總結

Android-onInterceptTouchEvent()和onTouchEvent()總結

老實說,這兩個小東東實在是太麻煩了,很不好懂,我自己那api文件都頭暈,在網上找到很多資料,才知道是怎麼回事,這裡總結一下,記住這個原則就會很清楚了:

1、onInterceptTouchEvent()是用於處理事件(類似於預處理,當然也可以不處理)並改變事件的傳遞方向,也就是決定是否允許Touch事件繼續向下(子控制元件)傳遞,一但返回True(代表事件在當前的viewGroup中會被處理),則向下傳遞之路被截斷(所有子控制元件將沒有機會參與Touch事件),同時把事件傳遞給當前的控制元件的onTouchEvent()處理;返回false,則把事件交給子控制元件的onInterceptTouchEvent()

2、onTouchEvent()用於處理事件,返回值決定當前控制元件是否消費(consume)了這個事件,也就是說在當前控制元件在處理完Touch事件後,是否還允許Touch事件繼續向上(父控制元件)傳遞,一但返回True,則父控制元件不用操心自己來處理Touch事件。返回true,則向上傳遞給父控制元件(注:可能你會覺得是否消費了有關係嗎,反正我已經針對事件編寫了處理程式碼?答案是有區別!比如ACTION_MOVE或者ACTION_UP發生的前提是一定曾經發生了ACTION_DOWN,如果你沒有消費ACTION_DOWN,那麼系統會認為ACTION_DOWN沒有發生過,所以ACTION_MOVE或者ACTION_UP就不能被捕獲。)

概念介紹

1、onInterceptTouchEvent()是用於處理事件(重點onInterceptTouchEvent這個事件是從父控制元件開始往子控制元件傳的,直到有攔截或者到沒有這個事件的view,然後就往回從子到父控制元件,這次是onTouch的)(類似於預處理,當然也可以不處理)並改變事件的傳遞方向,也就是決定是否允許Touch事件繼續向下(子控制元件)傳遞,一但返回True(代表事件在當前的viewGroup中會被處理),則向下傳遞之路被截斷(所有子控制元件將沒有機會參與Touch事件),同時把事件傳遞給當前的控制元件的onTouchEvent()處理;返回false,則把事件交給子控制元件的onInterceptTouchEvent()

2、onTouchEvent()用於處理事件(重點onTouch這個事件是從子控制元件回傳到父控制元件的,一層層向下傳),返回值決定當前控制元件是否消費(consume)了這個事件,也就是說在當前控制元件在處理完Touch事件後,是否還允許Touch事件繼續向上(父控制元件)傳遞。返回false,則向上傳遞給父控制元件,詳細一點就是這個touch事件就給了父控制元件,那麼後面的up事件就是到這裡touch觸發,不會在傳給它的子控制元件。如果父控制元件依然是false,那touch的處理就給到父控制元件的父控制元件,那麼up的事件處理都在父控制元件的父控制元件,不會觸發下面的。

返回true,如果是子控制元件返回true,那麼它的touch事件都在這裡處理,父控制元件是處理不了,因為它收不到子控制元件傳給他的touch,被子控制元件給攔截了。(這裡囉嗦了這麼多就是為了加深記憶,這個兩個事件理解起來都這麼麻煩了,更何況去記,記我肯定是一下子就忘的了^0^)

(注:可能你會覺得是否消費了有關係嗎,反正我已經針對事件編寫了處理程式碼?答案是有區別!比如ACTION_MOVE或者ACTION_UP發生的前提是一定曾經發生了ACTION_DOWN,如果你沒有消費ACTION_DOWN,那麼系統會認為ACTION_DOWN沒有發生過,所以ACTION_MOVE或者ACTION_UP就不能被捕獲。)

詳細介紹

onInterceptTouchEvent()是ViewGroup的一個方法,目的是在系統向該ViewGroup及其各個childView觸發onTouchEvent()之前對相關事件進行一次攔截,Android這麼設計的想法也很好理解,由於ViewGroup會包含若干childView,因此需要能夠統一監控各種touch事件的機會,因此純粹的不能包含子view的控制元件是沒有這個方法的,如LinearLayout就有,TextView就沒有。

onInterceptTouchEvent()使用也很簡單,如果在ViewGroup裡覆寫了該方法,那麼就可以對各種touch事件加以攔截。但是如何攔截,是否所有的touch事件都需要攔截則是比較複雜的,touch事件在onInterceptTouchEvent()和onTouchEvent以及各個childView間的傳遞機制完全取決於onInterceptTouchEvent()和onTouchEvent()的返回值。並且,針對down事件處理的返回值直接影響到後續move和up事件的接收和傳遞。

關於返回值的問題,基本規則很清楚,如果return true,那麼表示該方法消費了此次事件,如果return false,那麼表示該方法並未處理完全,該事件仍然需要以某種方式傳遞下去繼續等待處理。


onInterceptTouchEvent()是ViewGroup的一個方法,目的是在系統向該ViewGroup及其各個childView觸發onTouchEvent()之前對相關事件進行一次攔截.

  1. down事件首先會傳遞到onInterceptTouchEvent()方法

  2. 如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return false,那麼後續的move, up等事件將繼續會先傳遞給該ViewGroup,之後才和down事件一樣傳遞給最終的目標view的onTouchEvent()處理。

  3. 如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return true,那麼後續的move, up等事件將不再傳遞給onInterceptTouchEvent(),而是和down事件一樣傳遞給該ViewGroup的onTouchEvent()處理,注意,目標view將接收不到任何事件。

  4. 如果最終需要處理事件的view的onTouchEvent()返回了false,那麼該事件將被傳遞至其上一層次的view的onTouchEvent()處理。

  5. 如果最終需要處理事件的view 的onTouchEvent()返回了true,那麼後續事件將可以繼續傳遞給該view的onTouchEvent()處理。

    僅僅看這個官方文件解釋,就能理解清楚這兩個函式關係以及用途的絕對是富有經驗的framework高手。
    否則,一定需要一個案例來闡釋。假設我們有這樣一個layout,非常典型的

    1. <com.test.LayoutView1xmlns:android="http://schemas.android.com/apk/res/android"
    2. android:orientation="vertical"android:layout_width="fill_parent"
    3. android:layout_height="fill_parent">
    4. <com.test.LayoutView2
    5. android:orientation="vertical"android:layout_width="fill_parent"
    6. android:layout_height="fill_parent"android:gravity="center">
    7. <com.test.MyTextView
    8. android:layout_width="wrap_content"android:layout_height="wrap_content"
    9. />
    10. </com.test.LayoutView2>
    11. </com.test.LayoutView1>

    用一個示例圖來解釋這個layout:


    通常外圍的layoutview1,layoutview2,只是佈局的容器不需要響應觸屏的點選事件,僅僅Mytextview需要相應點選。但這只是一般情況,一些特殊的佈局可能外圍容器也要響應,甚至不讓裡面的mytextview去響應。更有特殊的情況是,動態更換響應物件。
    那麼首先看一下預設的觸屏事件的在兩個函式之間的傳遞流程。如下圖:


    如果僅僅想讓MyTextView來響應觸屏事件,讓MyTextView的OnTouchEvent返回true,那麼事件流就變成如下圖,可以看到layoutview1,layoutview2已經不能進入OnTouchEvent:


    另外一種情況,就是外圍容器想獨自處理觸屏事件,那麼就應該在相應的onInterceptTouchEvent函式中返回true,表示要截獲觸屏事件,比如layoutview1作截獲處理,處理流變成如下圖:


    以此類推,我們可以得到各種具體的情況,整個layout的view類層次中都有機會截獲,而且能看出來外圍的容器view具有優先截獲權。

    當我們去做一些相對來講具有更復雜的觸屏互動效果的應用時候,經常需要動態變更touch event的處理物件,比如launcher待機桌面和主選單(見下圖),從滑動螢幕開始到停止滑動過程當中,只有外圍的容器view才可以處理touch event,否則就會誤點選上面的應用圖示或者widget.反之在靜止不動的狀態下則需要能夠響應圖示(子view)的touch事件。摘取framework中abslistview程式碼如下

    1. publicboolean onInterceptTouchEvent(MotionEvent ev){
    2. int action = ev.getAction();
    3. switch(action &MotionEvent.ACTION_MASK){
    4. caseMotionEvent.ACTION_DOWN:{
    5. if(touchMode == TOUCH_MODE_FLING){
    6. returntrue;//fling狀態,截獲touch,因為在滑動狀態,不讓子view處理
    7. }
    8. break;
    9. }
    10. caseMotionEvent.ACTION_MOVE:{
    11. switch(mTouchMode){
    12. case TOUCH_MODE_DOWN:
    13. finalint pointerIndex = ev.findPointerIndex(mActivePointerId);
    14. finalint y =(int) ev.getY(pointerIndex);
    15. if(startScrollIfNeeded(y - mMotionY)){
    16. returntrue;//開始滑動狀態,截獲touch事件,不讓子view處理
    17. }
    18. break;
    19. }
    20. break;
    21. }
    22. }



    總結:

    僅僅通過概覽性的官方文件是很難理解onInterceptTouchEvent函式的用途的,只有通過演繹這個抽象的規則,配以圖文才能獲取這個重要的知識。很顯然,預設是返回false,不做截獲。返回true之後,事件流的後端控制元件就沒有機會處理touch事件了,把預設的事件流中每個處理函式看作一個節點,這個節點只要返回true, 後續的事件就被截止了,這樣想就很好理解。

    onInterceptTouchEvent是在ViewGroup裡面定義的。Android中的layout佈局類一般都是繼承此類的。onInterceptTouchEvent是用於攔截手勢事件的,每個手勢事件都會先呼叫onInterceptTouchEvent。 
    onInterceptTouchEvent()用於處理事件並改變事件的傳遞方向。返回值為false時事件會傳遞給子控制元件的onInterceptTouchEvent();返回值為true時事件會傳遞給當前控制元件的onTouchEvent(),而不在傳遞給子控制元件,這就是所謂的Intercept(截斷)。
    onTouchEvent() 用於處理事件,返回值決定當前控制元件是否消費(consume)了這個事件。可能你要問是否消費了又區別嗎,反正我已經針對事件編寫了處理程式碼?答案是有區別!比如ACTION_MOVE或者ACTION_UP發生的前提是一定曾經發生了ACTION_DOWN,如果你沒有消費ACTION_DOWN,那麼系統會認為ACTION_DOWN沒有發生過,所以ACTION_MOVE或者ACTION_UP就不能被捕獲。

ViewGroup裡的onInterceptTouchEvent預設值是false這樣才能把事件傳給View裡的onTouchEvent.

ViewGroup裡的onTouchEvent預設值是false。

View裡的onTouchEvent返回預設值是true.這樣才能執行多次touch事件。