30分鐘搞清楚Android Touch事件分發機制_Android

來源:互聯網
上載者:User

Touch事件分發中只有兩個主角:ViewGroup和View。Activity的Touch事件事實上是調用它內部的ViewGroup的Touch事件,可以直接當成ViewGroup處理。

View在ViewGroup內,ViewGroup也可以在其他ViewGroup內,這時候把內部的ViewGroup當成View來分析。

ViewGroup的相關事件有三個:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相關事件只有兩個:dispatchTouchEvent、onTouchEvent。

先分析ViewGroup的處理流程:首先得有個結構模型概念:ViewGroup和View組成了一棵樹形結構,最頂層為Activity的ViewGroup,下面有若干的ViewGroup節點,每個節點之下又有若干的ViewGroup節點或者View節點,依次類推。如圖:

當一個Touch事件(觸摸事件為例)到達根節點,即Acitivty的ViewGroup時,它會依次下發,下發的過程是調用子View(ViewGroup)的dispatchTouchEvent方法實現的。簡單來說,就是ViewGroup遍曆它包含著的子View,調用每個View的dispatchTouchEvent方法,而當子View為ViewGroup時,又會通過調用ViwGroup的dispatchTouchEvent方法繼續調用其內部的View的dispatchTouchEvent方法。上述例子中的訊息下發順序是這樣的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只負責事件的分發,它擁有boolean類型的傳回值,當返回為true時,順序下發會中斷。在上述例子中如果⑤的dispatchTouchEvent返回結果為true,那麼⑥-⑦-③-④將都接收不到本次Touch事件。來個簡單版的代碼加深理解:
 

/**   * ViewGroup   * @param ev   * @return   */  public boolean dispatchTouchEvent(MotionEvent ev){    ....//其他處理,在此不管    View[] views=getChildView();    for(int i=0;i<views.length;i++){      //判斷下Touch到螢幕上的點在該子View上面       if(...){      if(views[i].dispatchTouchEvent(ev))       return true;       }    }    ...//其他處理,在此不管  }  /**   * View   * @param ev   * @return   */  public boolean dispatchTouchEvent(MotionEvent ev){    ....//其他處理,在此不管    return false;  }

在此可以看出,ViewGroup的dispatchTouchEvent是真正在執行“分發”工作,而View的dispatchTouchEvent方法,並不執行分發工作,或者說它分發的對象就是自己,決定是否把touch事件交給自己處理,而處理的方法,便是onTouchEvent事件,事實上子View的dispatchTouchEvent方法真正執行的代碼是這樣的

/**   * View   * @param ev   * @return   */  public boolean dispatchTouchEvent(MotionEvent ev){    ....//其他處理,在此不管    return onTouchEvent(event);  }

一般情況下,我們不該在普通View內重寫dispatchTouchEvent方法,因為它並不執行分發邏輯。當Touch事件到達View時,我們該做的就是是否在onTouchEvent事件中處理它。

那麼,ViewGroup的onTouchEvent事件是什麼時候處理的呢?當ViewGroup所有的子View都返回false時,onTouchEvent事件便會執行。由於ViewGroup是繼承於View的,它其實也是通過調用View的dispatchTouchEvent方法來執行onTouchEvent事件。

 

在目前的情況看來,似乎只要我們把所有的onTouchEvent都返回false,就能保證所有的子控制項都響應本次Touch事件了。但必須要說明的是,這裡的Touch事件,只限於Acition_Down事件,即觸摸按下事件,而Aciton_UP和Action_MOVE卻不會執行。事實上,一次完整的Touch事件,應該是由一個Down、一個Up和若干個Move組成的。Down方式通過dispatchTouchEvent分發,分發的目的是為了找到真正需要處理完整Touch請求的View。當某個View或者ViewGroup的onTouchEvent事件返回true時,便表示它是真正要處理這次請求的View,之後的Aciton_UP和Action_MOVE將由它處理。當所有子View的onTouchEvent都返回false時,這次的Touch請求就由根ViewGroup,即Activity自己處理了。

看看改進後的ViewGroup的dispatchTouchEvent方法

View mTarget=null;//儲存捕獲Touch事件處理的View  public boolean dispatchTouchEvent(MotionEvent ev) {    //....其他處理,在此不管        if(ev.getAction()==KeyEvent.ACTION_DOWN){      //每次Down事件,都置為Null      if(!onInterceptTouchEvent()){      mTarget=null;      View[] views=getChildView();      for(int i=0;i<views.length;i++){        if(views[i].dispatchTouchEvent(ev))          mTarget=views[i];          return true;      }     }    }    //當子View沒有捕獲down事件時,ViewGroup自身處理。這裡處理的Touch事件包含Down、Up和Move    if(mTarget==null){      return super.dispatchTouchEvent(ev);    }    //...其他處理,在此不管    if(onInterceptTouchEvent()){     //...其他處理,在此不管       }//這一步在Action_Down中是不會執行到的,只有Move和UP才會執行到。    return mTarget.dispatchTouchEvent(ev);  }

ViewGroup還有個onInterceptTouchEvent,看名字便知道這是個攔截事件。這個攔截事件需要分兩種情況來說明:

1.假如我們在某個ViewGroup的onInterceptTouchEvent中,將Action為Down的Touch事件返回true,那便表示將該ViewGroup的所有下發操作攔截掉,這種情況下,mTarget會一直為null,因為mTarget是在Down事件中賦值的。由於mTarge為null,該ViewGroup的onTouchEvent事件被執行。這種情況下可以把這個ViewGroup直接當成View來對待。

2.假如我們在某個ViewGroup的onInterceptTouchEvent中,將Acion為Down的Touch事件都返回false,其他的都返回True,這種情況下,Down事件能正常分發,若子View都返回false,那mTarget還是為空白,無影響。若某個子View返回了true,mTarget被賦值了,在Action_Move和Aciton_UP分發到該ViewGroup時,便會給mTarget分發一個Action_Delete的MotionEvent,同時清空mTarget的值,使得接下去的Action_Move(如果上一個操作不是UP)將由ViewGroup的onTouchEvent處理。

情況一用到的比較多,情況二個人還未找到使用情境。

從頭到尾總結一下:

1.Touch事件分發中只有兩個主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三個相關事件。View包含dispatchTouchEvent、onTouchEvent兩個相關事件。其中ViewGroup又繼承於View。

2.ViewGroup和View組成了一個樹狀結構,根節點為Activity內部包含的一個ViwGroup。

3.觸摸事件由Action_Down、Action_Move、Aciton_UP組成,其中一次完整的觸摸事件中,Down和Up都只有一個,Move有若干個,可以為0個。

4.當Acitivty接收到Touch事件時,將遍曆子View進行Down事件的分發。ViewGroup的遍曆可以看成是遞迴的。分發的目的是為了找到真正要處理本次完整觸摸事件的View,這個View會在onTouchuEvent結果返回true。

5.當某個子View返回true時,會中止Down事件的分發,同時在ViewGroup中記錄該子View。接下去的Move和Up事件將由該子View直接進行處理。由於子View是儲存在ViewGroup中的,多層ViewGroup的節點結構時,上級ViewGroup儲存的會是真實處理事件的View所在的ViewGroup對象:如ViewGroup0-ViewGroup1-TextView的結構中,TextView返回了true,它將被儲存在ViewGroup1中,而ViewGroup1也會返回true,被儲存在ViewGroup0中。當Move和UP事件來時,會先從ViewGroup0傳遞至ViewGroup1,再由ViewGroup1傳遞至TextView。

6.當ViewGroup中所有子View都不捕獲Down事件時,將觸發ViewGroup自身的onTouch事件。觸發的方式是調用super.dispatchTouchEvent函數,即父類View的dispatchTouchEvent方法。在所有子View都不處理的情況下,觸發Acitivity的onTouchEvent方法。

7.onInterceptTouchEvent有兩個作用:1.攔截Down事件的分發。2.中止Up和Move事件向目標View傳遞,使得目標View所在的ViewGroup捕獲Up和Move事件。
 補充:

“觸摸事件由Action_Down、Action_Move、Aciton_UP組成,其中一次完整的觸摸事件中,Down和Up都只有一個,Move有若干個,可以為0個。”,這裡補充下其實UP事件是可能為0個的。
以上就是本文的全部內容,希望對大家理解Touch事件分發機制有所協助。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.