1. 程式人生 > >事件分發機制與滑動衝突

事件分發機制與滑動衝突

事件分發機制

事件分發過程主要由dispatchTouchEventonInterceptTouchEventonTouchEvent 三個方法共同完成:

  1. public boolean dispatchTouchEvent(MotionEvent ev)

    用來進行事件分發,如果事件能夠傳遞給當前view,則此方法一定呼叫,返回結果受當前view的onTouchEvent和下級view的dispatchTouchEvent方法影響,表示是否消耗了當前事件

  2. public boolean onInterceptTouchEvent(MotionEvent event)

    在dispatchTouchEvent內部呼叫,用來判斷是否攔截某個事件

  3. public boolean onTouchEvent(MotionEvent event)

    在dispatchTouchEvent內部呼叫,用來處理點選事件,返回結果表示是否消耗了當前事件,如果不消耗則在同一事件序列中,當前view無法再次接受到事件

點選事件達到頂級View(一般為ViewGroup)後,會呼叫viewGroup的dispatchTouchEvent方法,如果頂級ViewGroup的onInterceptTouchEvent返回true,則事件由ViewGroup處理,這時如果ViewGroup的mOnTouchListener被設定,則onTouch會被呼叫,onTouch若返回true,ViewGroup的onTouchEvent不會再被呼叫;若設定了mOnClickListener,則onClick會被呼叫。如果頂層ViewGroup不攔截事件即onInterceptTouchEvent

返回false,則事件傳遞給子View,這時子View的dispatchTouchEvent會被呼叫,到此為止,事件已經從頂級View傳遞了下一層View,如此迴圈,完成整個事件的分發。

如果一個View的onTouchEvent返回false,那麼它的父容器的onTouchEvent將會被呼叫,表示無法消耗(解決)事件,需要交給上級。

ViewGroup預設不攔截任何事件,即Android原始碼中ViewGroup的onInterceptTouchEvent預設返回false

View沒有onInterceptTouchEvent方法,一旦有點選事件傳遞給它,那麼它的onTouchEvent

方法就會被呼叫。

  • 虛擬碼助記:
public boolean dispatchTouchEvent(MotionEvent ev){
    boolean result= false;
    if(onInterceptTouchEvent(ev)){
         result = onTouchEvent(ev);
    }else{
        result = child.dispatchTouchEvent(ev);
    }
    return result;
}
  • 一張圖說明事件分發機制:

這裡寫圖片描述

滑動衝突

在介面中內外兩層同時可以滑動,就產生了滑動衝突,如何解決滑動衝突呢?其實這是一件非常簡單的事,因為滑動衝突的解決有固定的套路,只要知道了這個固定套路,問題就十分容易解決了。

常見的滑動衝突場景可分為以下三種:

  1. 外部滑動方向和內部滑動方向不一致

    ViewPager和Fragment組成的頁面中包括一個ListView,這種情況是有滑動衝突的,但是ViewPager內部處理了這種滑動衝突,因此採用ViewPager時無須關注這個問題,如果採用的不是ViewPager而是ScrollView,那就必須處理滑動衝突了,否則內外兩層就只有一層能滑動

  2. 外部滑動方向和內部滑動方向一致

    內外兩層在同一個方向都可以滑動,系統無法知道使用者到底想讓哪一層滑動

  3. 上面兩種情況的巢狀

    比如SlideMenu、ScrollView和ListView三者同時出現

處理規則

場景1:根據滑動是水平滑動還是豎直滑動來判斷到底由誰來攔截事件

場景2:雖然無法根據滑動方向來判斷,但是這時一般在業務上有狀態可以依賴,比如業務上有規定,當處於某種狀態時需要外部View響應使用者滑動,而處於另一種狀態時需要內部View響應滑動

場景3:其實很簡單,結合場景1和場景2的處理規則,就能處理場景3的滑動衝突

解決方式

1.外部攔截法
點選事件先經過父容器的攔截處理,如果父容器需要響應就攔截,不需要就不攔截,攔截處理需要寫在父容器的onInterceptTouchEvent方法裡,至於原因可以回顧 徹底理解view事件分發機制,這是解決滑動衝突的必要基礎

2.內部攔截法
父容器不攔截任何事件,子view需要響應就直接消耗,否則就交由父容器處理

這就是Android 滑動衝突的所有姿勢~ 是不是非常簡單?只要你真的理解View事件分發機制,那滑動衝突就像紙老虎一樣一捅就破~

完~

參考書籍《Android開發藝術探索》