1. 程式人生 > >Android 事件分發,一些被遺漏忽略的細節

Android 事件分發,一些被遺漏忽略的細節

1, 介面佈局 Linear Layout 包裹一個TextView。textView 上設定了按壓效果。

問:當手指從TextView 上按下,然後移動手指到TextView 邊界以外,發生那些事件?

佈局檔案如下:

<test.lee.carlyle.com.myapplication.touch.MyLinear xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="horizontal"
    android:id="@+id/linear"

    >

    <test.lee.carlyle.com.myapplication.touch.MYTextView
        android:layout_width="80dp"
        android:layout_height="50dp"
        android:background="@drawable/bg_sl"
        android:layout_margin="10dp"
        android:text="GOOD 0"
        />

    <test.lee.carlyle.com.myapplication.touch.MYTextView
        android:layout_width="40dp"
        android:layout_height="30dp"
        android:background="@drawable/bg_sl"
        android:layout_margin="10dp"
        android:text="GOOD 1"
        />

</test.lee.carlyle.com.myapplication.touch.MyLinear>

 

你以為 介面上的效果是這樣???:《按壓時 TextView 變色。 拖拽過程中,當手指離開textView 後,顏色恢復到normal 狀態。》

但是你忽略掉了一點:當沒有給TextView 設定事件的時候事件流程狀態。

  • 當TextView 沒有點選事件的時候,OnTouchEvent down 返回false 。 之後不再相應按壓事件。也沒有按壓效果。
  • 當在TextView 上設定了點選事件後,有了按壓效果。

2,事件執行流程:

10-28 17:07:25.957 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action down 
    text View 0 action down 
10-28 17:07:25.963 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action move 
    text View 0 action move 
10-28 17:07:26.260 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action move 
    text View 0 action move 
10-28 17:07:26.276 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action move 
10-28 17:07:26.277 20796-20796/test.lee.carlyle.com.myapplication E/motion: text View 0 action move 
10-28 17:07:26.292 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action move 
    text View 0 action move 
10-28 17:07:26.293 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action up 
10-28 17:07:26.294 20796-20796/test.lee.carlyle.com.myapplication E/motion: text View 0 action up 

中間省略了一堆move 日誌。 可以看到在手指移出TextView 的範圍之後,仍然還將move 事件繼續分發到當前touch 焦點的試圖上。但是在手指移出textView 檢視的時候,按壓效果消失了

之前一直以為這個drawable state change 是因為parent 發出了 state cacel 事件導致的。並以為事件將不再傳入。因為覺得合理就沒有深究。。。

那麼,既然不是 cancel 事件,那麼這個 drawable state pressed 狀態是怎麼消失的呢?

看程式碼:

原來是View 自己在move 的時候自己處理的。

 

3, 問題擴充套件:

  • 當在父佈局中 dispatchTouchEvent 的時候,直接返回false 會怎樣? 為了驗證這個問題,我將佈局層級修改了一下,在當前佈局的外層,再增加了一層LinearLayout
  1. 在dispatch TouchEvent ActionDown 的時候直接返回false: 事件將不會繼續從這層的LinearLayout 繼續分發。但是 ActionDown事件卻一次傳遞到TextView 上了。導致TextView press state change。但是無法恢復了。由於當前ViewGroup 層不分發事件,因此,事件在當前層的上層,直接交給OnTouchEvent 進行處理
    1. 如果上層(TextView -> parent -> parent)的ViewGroup 的OnTouchEvent down 事件返回了ture : 後續事件將保持這個流程一直傳遞下來。
    2. 如果上層的ViewGroup 的OnTouchEvent down 事件返回了false:事件將繼續交給上層的上層的onTouchEvent來判斷。

            可見:觸控事件流程是會在檢視樹中,的確會確定一個處理事件的View路徑的。在上層onTouch處理後,是不會再分發給下層的dispatch 去嘗試處理的。

<LinearLayout dispatchTouchDown= true; onTouchDown= true>
  <LinearLayout dispatchTouchDown= false; onTouchDown= false>
     <TextView/>
  </LinearLayout>
</LinearLayout>

日誌如下:
10-28 17:48:40.376 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action down 
    MyLinear dispatch 2131165251 action down 
    text View 0 action down 
10-28 17:48:40.378 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear  onTouch -1 action down 
10-28 17:48:40.388 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action move 
    MyLinear  onTouch -1 action move 
10-28 17:48:40.434 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action move 
    MyLinear  onTouch -1 action move 
10-28 17:48:40.450 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action move 
    MyLinear  onTouch -1 action move 
10-28 17:48:41.112 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action move 
    MyLinear  onTouch -1 action move 
10-28 17:48:41.128 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action move 
10-28 17:48:41.129 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear  onTouch -1 action move 
10-28 17:48:41.130 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action up 
    MyLinear  onTouch -1 action up 
  1. 在dispatch TouchEvent 非ActionDown 的時候返回false:此時不影響分發流程

       實驗如下:

<LinearLayout dispatchTouchDown= true; onTouchDown= true>
  <LinearLayout dispatchTouchDown= true; dispatchTouchMove = false; onTouchDown= false>
     <TextView onCLickListenter = true />
  </LinearLayout>
</LinearLayout>

        日誌顯示,事件都將由TextView 進行處理。外層一致dispatch 到當前層的TextView 上。然後然後交由TextView 來處理。

        疑問: 如該在TextView 中,onTouchEvent  down 返回true, 其他的返回false , 又會如何呢?

        實驗如下:

<LinearLayout dispatchTouchDown= true; onTouchDown= true>
  <LinearLayout dispatchTouch= true; onTouchDown= false>
     <TextView onCLickListenter = true ; onTouchDown= true ; onTouchMove = false />
  </LinearLayout>
</LinearLayout>

        實驗結果:TextView 的OnTouchEvent 在actionDown 之外返回false。並不影響事件傳遞流程

        結論:只有actionDown 事件中返回true 或 fase 會影響檢視樹的事件傳遞流程。

  •        當在FrameLayout 中巢狀兩個ViewGroup. top 層 down 先返回true , 然後返回false ;  bottom 層,一直返回true . 那麼事件傳遞流程會怎樣?

三、原始碼分析:

    事件傳遞樹是如何構成的?在ViewGroup 中有一個屬性。這是一個鏈式結構。

private TouchTarget mFirstTouchTarget;

確定View tree 事件傳遞連結的程式碼在這裡:

 

這個函式在 ViewGroup的dispatchTouchEvent 中的 ActionDown 事件中執行。在此時將確定本層的TouchTarget連結串列。

其中關鍵方法:dispatchTransformedTouchEvent.  這個方法確定了是向下層分發(child.dispatch)還是向同層分發(super.dispatch:View.dispatchTouchEvent())。

    可見在View.dispatchTouchEvent 中,呼叫了View.OnTouchEvent.  而View.onTouchEvent 的返回值只有在ActionDown 事件時,才能通過是否View 作為TouchTarget 新增到TouchTarget list 中的方式影響到事件分發流程

 

        一路向上:我們來到Activity dispatchTouchEvent 的程式碼中看看。

跟蹤程式碼發現。getWindow().superDispatchTouchEvent() 實際的處理者是 PhoneWidnow 中的DecoreView(即ViewGroup).dispatchTouchEvent()。