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

注意:本文中所有源碼分析部分均基于 API23(Android 6.0)

一 事件

事件 簡介
ACTION_DOWN 手指 初次接觸到屏幕 時觸發(fā)。
ACTION_MOVE 手指 在屏幕上滑動 時觸發(fā),會會多次觸發(fā)。
ACTION_UP 手指 離開屏幕 時觸發(fā)。
ACTION_CANCEL 事件 被上層攔截 時觸發(fā)。

對于單指觸控來說,一次簡單的交互流程是這樣的:

手指落下(ACTION_DOWN) -> 移動(ACTION_MOVE) -> 離開(ACTION_UP)

ACTION_MOVE 有多次觸發(fā)。
如果僅僅是單擊,不會觸發(fā) ACTION_MOVE。

二 事件分發(fā)、攔截與消費(fèi)

下表省略了 PhoneWidow 和 DecorView。

√ 表示有該方法。
X 表示沒有該方法。

類型 方法 activity viewGroup view
事件分發(fā) dispatchTouchEvent
事件攔截 onInterceptTouchEvent X X
事件消費(fèi) onTouchEvent

1 方法功能介紹

  1. dispatchTouchEvent 事件分發(fā)
    activity,view,viewGroup都擁有該方法
  2. onInterceptTouchEvent 事件攔截
    viewGroup獨(dú)有的方法,負(fù)責(zé)如果viewGroup需要攔截事件,此函數(shù)返回true,交于viewGroup處理
  3. onTouchEvent

這個三個方法均有一個 boolean(布爾) 類型的返回值,通過返回 true 和 false 來控制事件傳遞的流程。
PS:從上表可以看到 Activity 和 View 都是沒有事件攔截的,這是因?yàn)椋?/p>

  1. Activity 作為原始的事件分發(fā)者,如果 Activity 攔截了事件會導(dǎo)致整個屏幕都無法響應(yīng)事件,這肯定不是我們想要的效果。
  2. View最為事件傳遞的最末端,要么消費(fèi)掉事件,要么不處理進(jìn)行回傳,根本沒必要進(jìn)行事件攔截。

2 事件分發(fā)、攔截與消費(fèi)三個方法執(zhí)行調(diào)用順序

viewGroup :分發(fā)->攔截->消費(fèi)
view :分發(fā)->消費(fèi)
Activity :分發(fā)->消費(fèi)

調(diào)用流程

事件調(diào)用流程.jpg

activity 源碼

activity #dispatchTouchEvent源碼

 public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
// 調(diào)用phoneWindow的superDispatchTouchEvent
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

activity #onTouchEvent

 public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        return false;
    }

2 view各個事件的調(diào)用順序

  1. 單擊事件(onClickListener) 需要兩個兩個事件(ACTION_DOWN 和 ACTION_UP )才能觸發(fā),如果先分配給onClick判斷,等它判斷完,用戶手指已經(jīng)離開屏幕,黃花菜都涼了,定然造成 View 無法響應(yīng)其他事件,應(yīng)該最后調(diào)用。(最后)
  2. 長按事件(onLongClickListener) 同理,也是需要長時間等待才能出結(jié)果,肯定不能排到前面,但因?yàn)椴恍枰狝CTION_UP,應(yīng)該排在 onClick 前面。(onLongClickListener > onClickListener)
  3. 觸摸事件(onTouchListener) 如果用戶注冊了觸摸事件,說明用戶要自己處理觸摸事件了,這個應(yīng)該排在最前面。(最前)
  4. View自身處理(onTouchEvent) 提供了一種默認(rèn)的處理方式,如果用戶已經(jīng)處理好了,也就不需要了,所以應(yīng)該排在 onTouchListener 后面。(onTouchListener > onTouchEvent)
    所以事件的調(diào)度順序應(yīng)該是
    onTouchListener > onTouchEvent > onLongClickListener > onClickListener。

三 事件分發(fā)流程

事件收集之后最先傳遞給 Activity, 然后依次向下傳遞,大致如下:

Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View

如果view沒有處理事件,再反向交回事件,判斷處理

Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View

核心要點(diǎn)

  1. 事件分發(fā)原理: 責(zé)任鏈模式,事件層層傳遞,直到被消費(fèi)。
  2. ** View 的 dispatchTouchEvent 主要用于調(diào)度自身的監(jiān)聽器和 onTouchEvent。**
  3. ** View的事件的調(diào)度順序是onTouchListener > onTouchEvent > onLongClickListener > onClickListener 。**
  4. ** 不論 View 自身是否注冊點(diǎn)擊事件,只要 View 是可點(diǎn)擊的就會消費(fèi)事件。**
  5. 事件是否被消費(fèi)由返回值決定,true 表示消費(fèi),false 表示不消費(fèi),與是否使用了事件無關(guān)。
  6. ViewGroup 中可能有多個 ChildView 時,將事件分配給包含點(diǎn)擊位置的 ChildView。
  7. ViewGroup 和 ChildView 同時注冊了事件監(jiān)聽器(onClick等),由 ChildView 消費(fèi)。
  8. 一次觸摸流程中產(chǎn)生事件應(yīng)被同一 View 消費(fèi),全部接收或者全部拒絕。
  9. 只要接受 ACTION_DOWN 就意味著接受所有的事件,拒絕 ACTION_DOWN 則不會收到后續(xù)內(nèi)容
  10. 如果當(dāng)前正在處理的事件被上層 View 攔截,會收到一個 ACTION_CANCEL,后續(xù)事件不會再傳遞過來。

六 參考資料

安卓自定義View進(jìn)階-事件分發(fā)機(jī)制原理

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

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

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