Android view觸摸事件分發dispatchTouchEvent 攔截 onInterceptTouchEvent 處理 onTouchEvent

MotionEvent.ACTION_DOWN//按下事件

MotionEvent.ACTION_MOVE//移動事件

//結束事件
MotionEvent.ACTION_UP://按下直接擡
MotionEvent.ACTION_CANCEL://事件被上層節點intercept了,我收不到event了,也就cancel了

然後,再瞭解,android的事件是怎麼傳播的

不多說概念,直接上流程
一 、MotionEvent.ACTION_DOWN(按下事件)

  1. 第一步當然是處理MotionEvent.ACTION_DOWN事件,由事件所在的 活動 調用dispatchTouchEvent(MotionEvent event)準備 分發這個按下事件(如果不被onInterceptTouchEvent攔截)事件會分發到view樹結構至事件發生的最小view節點的方法中,這一相關的view節點樹結構,我在此文章稱之爲 事件分發view節點樹
  2. 事件分發到ViewGroup類型的節點時,節點會接收到Activity發來的按下事件會 依序 分發到自身的2個方法 中,方法如下所示
    1. public boolean dispatchTouchEvent(MotionEvent event)//此方法默認返回super.dispatchTouchEvent(event)
    2. public boolean onInterceptTouchEvent(MotionEvent ev)//如果在此方法中返回true就代表攔截事件分發,也就意味事件分發在此類型爲viewGroup的view節點中斷,不會繼續沿着事件分發view節點樹向下分發此事件。
  3. 如果一切順利沒人攔截事件分發到最小View節點時,節點會調用最小節點的如下所示方法
    1. dispatchTouchEvent(MotionEvent event)
  4. 如果事件分發view節點樹沒被攔截,那就是由最小view節點中的public boolean onTouchEvent(MotionEvent event)作爲起始節點沿着 事件分發view節點樹向上分發,但是如果被攔截了,就由攔截的viewGroup節點public boolean onTouchEvent(MotionEvent event)作爲起始節點, 準備 把此按下事件向上分發事件分發view節點樹 上的每個節點的public boolean onTouchEvent(MotionEvent event)中,直到分發到根節點也就是Activity的public boolean onTouchEvent(MotionEvent event)方法中爲止。
    但是隻要有任意一個節點在自身的public boolean onTouchEvent(MotionEvent event)方法中返回true,那麼事件向上分發就會在此節點中斷,也就意味着事件在事件分發view節點樹上就只向上分發到此節點的public boolean onTouchEvent(MotionEvent event)方法中爲止,不會繼續向上級節點分發這此事件。MotionEvent.ACTION_DOWN(用戶按下)事件分發就此結束。

二、MotionEvent.ACTION_MOVE(移動事件)
第一步當然還是activity的事件分發dispatchTouchEvent(MotionEvent event),當然這次分發的是移動事件,不是按下事件,如果在MotionEvent.ACTION_DOWN(用戶按下)按下事件分發流程完畢後,事件分發view節點樹上沒有任何一個節點在自身的public boolean onTouchEvent(MotionEvent event)方法返回true的話,就意味着沒有view節點處理,那麼Activity只會把事件分發給自身的public boolean onTouchEvent(MotionEvent event),反之,如果有某個節點在MotionEvent.ACTION_DOWN(用戶按下)事件分發流程中在自身的public boolean onTouchEvent(MotionEvent event)返回true的話就意味着這移動事件在這個view節點中要被處理,用專業的詞就就是移動事件要在此節點中被消費。所以activiy會把此移動事件根據事件分發view節點樹分發至此返回true的節點爲止,分發到此移動事件的viewGroup類型節點都會把這個移動事件依序發發到自身的 2 個方法中,方法如下所示。

  1. public boolean dispatchTouchEvent(MotionEvent event)
  2. public boolean onInterceptTouchEvent(MotionEvent event)//可以用此方法攔截移動事件,攔截之後,Activity重新分發移動事件直到此view節點的onTouchEvent爲止。
    但如果分發到事件的節點是view類型的節點(不太確定,想攔截事件直接在oninter返回true就行了)或者是在onInterceptTouchEvent返回ture的節點的上級節點,那麼就只會調用以下所示方法(dispaev),通俗的說就是事件以被此節點攔截了,事件只會分發到此節點的上級包括自身的dispatchTouchEvent方法,不會調用節點onInterceptEvent方法,最後發送到自身節點的onTouchEvent方法爲止,無論返回什麼值都不會繼續傳到上級節點的ontouchevent。爲什麼呢?一個事件被攔截了,就代表此由此節點處理接下來的手勢了,上級節點以及下級節點都能不處理了,如果下個move事件再次分發經過此上級節點事件如果能分發到onInterceptTouchevent或下級節點收到分發事件還有上級的ontouchevent,就會使當前想處理手勢的節點無法正常接收move事件進行手勢處理。
    public boolean dispatchTouchEvent(MotionEvent event)
    事件最後會分發給返回true的節點,此節點會調用自身的兩個方法如下所示,最後由節點的public boolean onTouchEvent(MotionEvent event)方法收到此移動事件後被調用後,此MotionEvent.ACTION_MOVE(移動事件)分發結束。不會繼續向上級節點的ontouchevent分發事件。
    分發結束後,我就可以在返回true的節點中的onTouchEvent方法,或 dispatchTouchEvent方法中進行觸摸的業邏輯開發。
  3. public boolean dispatchTouchEvent(MotionEvent event)
  4. public boolean onTouchEvent(MotionEvent event)

三、MotionEvent.ACTION_UP、MotionEvent.ACTION_CANCEL(結束事件)
結束事件的分發同 移動事件分發一樣,在按下事件中onTouchEvent返回true的 節點中的onTouchEvent中爲止。結束事件分發與移動事件分發同理

原理圖示如下
在這裏插入圖片描述

再說明一個知識點
當一個view層級不夠,被一個遮擋view遮擋住自身view時,如果這個遮擋view的在ontouchEvent MotionEvetnt.Action_Donwn中返回true。這個被遮擋的view就收不到Acton_move事件。因爲遮擋view在接收onTouchEvent ACTION_DOWN事件要比被遮擋view先收到。(放棄坑太深了,action_move也不能放回true )
如圖所示
在這裏插入圖片描述

參考文章
事件分發

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章