View事件分發(fā)機(jī)制

前言

· 在Android自定義View/ViewGroup中,我們免不了出現(xiàn)它的觸摸事件,所以了解View的事件分發(fā)非常重要
· 本人菜雞水平,寫(xiě)此文章為了加深印象,如果發(fā)現(xiàn)錯(cuò)誤或不足懇請(qǐng)指正。謝謝

講在前面

在事件分發(fā)過(guò)程中,我們首先要知道View的事件分發(fā)是要區(qū)分View和ViewGroup

來(lái)張圖

View的事件分發(fā)

view事件分發(fā)中的三個(gè)重要方法:
1.dispatchTouchEvent()
2.onTouch()
3.onTouchEvent()

·我看首先看看事件在View是如何傳遞和消費(fèi)的
DispatchTouchEvent()部分代碼,也是核心代碼

 boolean result = false;
 ListenerInfo li = mListenerInfo;
     if (li != null && li.mOnTouchListener != null
               && (mViewFlags & ENABLED_MASK) == ENABLED
               && li.mOnTouchListener.onTouch(this, event)) {
           result = true;
       }
    if (!result && onTouchEvent(event)) {
           result = true;
       }

首先會(huì)判斷View是否設(shè)置了觸摸事件,如果設(shè)置了點(diǎn)擊事件,并且并且當(dāng)前View是可點(diǎn)擊的,那么就會(huì)回調(diào)li.mOnTouchListener.onTouch(this, event)),如果此方法返回true,那么就不會(huì)調(diào)用onTouchEvent(event)這個(gè)方法了,說(shuō)明這個(gè)觸摸事件被消費(fèi)了。如果返回false,那么就會(huì)回調(diào)onTouchEvent(event)這個(gè)方法,如果返回true,事件被銷毀。返回false代表事件繼續(xù)向下傳遞。由此可見(jiàn)執(zhí)行優(yōu)先級(jí):onTouch() >onTouchEvent()。
然后我們看看onTouchEvent()這個(gè)方法
是switch case語(yǔ)句我們不分析,各種事件了。我們主要看看UP中:

  case MotionEvent.ACTION_UP:
        //......
              if (!focusTaken) {
                      if (mPerformClick == null) {
                              mPerformClick = new PerformClick();
                      }
                      if (!post(mPerformClick)) {
                              performClickInternal();
                      }
              }     
       //......
              break;

我們?cè)诖蜷_(kāi)performClickInternal看看,最后就是performClick()這個(gè)方法

public boolean performClick() {
        //......
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }
        //......
        return result;
    }

通過(guò)源碼我們可以看到,onClickListener.onClick()方法其實(shí)就是onTouchEvent中up事件中被調(diào)用,所以執(zhí)行優(yōu)先級(jí):onTouchEvent() > onClick()。如果你在執(zhí)行ACTION_DOWN的時(shí)候返回了false,后面一系列其它的action就不會(huì)再得到執(zhí)行了。簡(jiǎn)單的說(shuō),就是當(dāng)dispatchTouchEvent在進(jìn)行事件分發(fā)的時(shí)候,只有前一個(gè)action返回true,才會(huì)觸發(fā)后一個(gè)action。View的事件分發(fā)就說(shuō)到這。

ViewGroup的事件分發(fā)

view事件分發(fā)中的三個(gè)重要方法:
1.dispatchTouchEvent()
2.onInterceptTouchEvent()
3.onTouchEvent()

首先我們看看ViewGroup的dispatchTouchEvent()這個(gè)方法,源碼太多,我們把核心的代碼提出來(lái)

public boolean dispatchTouchEvent(MotionEvent ev){
  //......
      boolean handled = false;
      final boolean intercepted;
      intercepted=onInterceptTouchEvent(event);
      if(intercepted)
            handled =onTouchEvent(event)
      else{
         final View[] children = mChildren;
         for (int i = childrenCount - 1; i >= 0; i--) //找到當(dāng)前觸摸位置的childView
      handled = child.dispatchTouchEvent(event);
      }
  //......
      return handled;
}

這個(gè)就是ViewGroup的邏輯,首先先判斷onInterceptTouchEvent(event)(默認(rèn)為false)如果為true,則攔截這個(gè)事件。調(diào)用viewgroup的onTouchEvent(event)來(lái)判斷是否消費(fèi)事件。否則向下傳遞。遍歷找到子View并把當(dāng)前事件交給子View調(diào)用子View的dispatchTouchEvent(event)。

public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return true;
        }
        return false;
    }

onInterceptTouchEvent()默認(rèn)返回false,代表viewGroup不攔截事件
1.當(dāng)事件傳遞到View時(shí),如果View的onTouchEvent()返回false,則父類的onTouchEvent()會(huì)被調(diào)用,依次向上傳遞
2.若所有的View都不消耗事件時(shí),Activity的onTouchEvent()會(huì)被調(diào)用

結(jié)論

1.對(duì)于View一旦決定攔截事件即onTouchEvent()返回true,那后續(xù)的整個(gè)事件序列都會(huì)交給它消耗;
2.執(zhí)行優(yōu)先級(jí):onTouch() > onTouchEvent() > onClick()
3.事件的傳遞是
Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View
如果沒(méi)有找到合適的View消費(fèi)事件:那么事件反向傳遞如果最后 Activity 也沒(méi)有處理,本次事件才會(huì)被拋棄
Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View


感謝閱讀,歡迎點(diǎn)贊和評(píng)論

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容