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,非常典型的


    用一個示例圖來解釋這個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. public boolean onInterceptTouchEvent(MotionEvent ev) {

    2.         int action = ev.getAction();

    3.         switch (action & MotionEvent.ACTION_MASK) {

    4.         case MotionEvent.ACTION_DOWN: {

    5.  

    6.             if (touchMode == TOUCH_MODE_FLING) {

    7.                 return true;  //fling狀態,截獲touch,因爲在滑動狀態,不讓子view處理

    8.             }

    9.             break;

    10.         }

    11.         case MotionEvent.ACTION_MOVE: {

    12.             switch (mTouchMode) {

    13.             case TOUCH_MODE_DOWN:

    14.                 final int pointerIndex = ev.findPointerIndex(mActivePointerId);

    15.                 final int y = (int) ev.getY(pointerIndex);

    16.                 if (startScrollIfNeeded(y - mMotionY)) {

    17.                     return true;//開始滑動狀態,截獲touch事件,不讓子view處理

    18.                 }

    19.                 break;

    20.             }

    21.             break;

    22.         }

    23. }

    24. <com.test.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"  

    25.     android:orientation="vertical" android:layout_width="fill_parent"  

    26.     android:layout_height="fill_parent">  

    27.     <com.test.LayoutView2  

    28.         android:orientation="vertical" android:layout_width="fill_parent"  

    29.         android:layout_height="fill_parent" android:gravity="center">  

    30.         <com.test.MyTextView  

    31.             android:layout_width="wrap_content"   android:layout_height="wrap_content"  

    32.       />  

    33.     </com.test.LayoutView2>  

    34. </com.test.LayoutView1>


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章