引言
從Android底層開始分析View的事件分發至上層FrameWork。
分兩部分來說:
- 觸碰屏幕傳遞事件至當前Activity.
- Activtiy傳遞事件至觸碰到的View 或者 ViewGroup
觸碰屏幕傳遞事件至當前Activity
觸摸事件是由Linux內核的一個Input子系統來管理的(InputManager),Linux子系統會在 /dev/input/ 這個路徑下創建硬件輸入設備節點(這里的硬件設備就是我們的觸摸屏了)。當手指觸動觸摸屏時,硬件設備通過設備節點像內核(其實是InputManager管理)報告事件,InputManager 經過處理將此事件傳給 Android系統的一個系統Service,這個Service叫WindowManagerService。之后WindowManagerService會將事件傳遞到PhoneWindow.
可以參考下圖, WindowManagerService連接PhoneWindow的過程.
WindowManagerService調用dispatchPointer()從存放WindowState的z-order順序列表中找到能接收當前touch事件的 WindowState,通過IWindow代理將此消息發送到IWindow服務端(IWindow.Stub子類),這個IWindow.Stub屬于ViewRoot(這個類繼承Handler,主要用于連接PhoneWindow和WindowManagerService),所以事件就傳到了ViewRoot.dispatchPointer()中.
看下ViewRoot.dispatchPointer method
你可以看到通過用Message將DISPATCH_POINTER事件發送出去,處理事件應該在handleMessage method里.
看下ViewRoot.handleMessage method
看下ViewRoot.deliverPointerEvent method
最終你會發現調用mView.dispatchTouchEvent(event)
(mView是一個PhoneWindow.DecorView對象),PhoneWindow.DecorView繼承FrameLayout(FrameLayout繼承ViewGroup,ViewGroup繼承自View),DecorView里的dispatchTouchEvent方法如下. 這里的Callback的cb其實就是Activity的attach()方法里的設置回調。
看下PhoneWindow.DecorView dispatchTouchEvent method
回調cb就代表Activity,回調會在Activity的onAttach的時候進行設置.
再看cb執行dispatchTouchEvent method.即執行Activity的dispatchTouchEvent,之后Activity會把事件又重新傳遞到DecorView,然后會調用父類(ViewGroup)的dispatchTouchEvent 將事件傳給父類處理。即調用ViewGroup 和 View的事件分發機制。
總算繞回到View ViewGroup的事件分發機制。
View 或者 ViewGroup的事件分發機制
事件的概念
在Android中,事件主要包括點按、長按、拖拽、滑動等,點按又包括單擊和雙擊,另外還包括單指操作和多指操作。所有這些都構成了Android中的事件響應。
事件分為三種:
- 按下(ACTION_DOWN)
- 移動(ACTION_MOVE)
- 抬起(ACTION_UP)
ViewGroup和View的分發
相關函數:
-
ViewGroup
- public boolean dispatchTouchEvent(MotionEvent event)
- public boolean onTouchEvent(MotionEvent event)
- public boolean onInterceptTouchEvent(MotionEvent event)
-
View
- public boolean dispatchTouchEvent(MotionEvent event)
- public boolean onTouchEvent(MotionEvent event)
ViewGroup和View的事件分發是向下傳遞的,即ViewGroup會一層層向子View分發事件,直到消費事件或者被丟棄。由此可以看出ViewGroup和View相關函數的返回類型都是Boolean,可以直到Boolean類型決定了某一事件是否是繼續往下傳,還是被攔截了,或是被消費了。
ViewGroup和View的相關函數都接受參數MotionEvent類型的參數,MotionEvent繼承于InputEvent,用于標記各種動作事件。之前提到的ACTIONDOWN、ACTIONMOVE、ACTION_UP都是MotinEvent中定義的常量。我們通過MotionEvent傳進來的事件類型來判斷接收的是哪一種類型的事件。
ViewGroup繼承View,即ViewGroup是一個特殊的View,ViewGroup在事件分發中比View多一個函數, onInterceptTouchEvent函數(攔截事件的函數),顧名思義,就是在ViewGroup一層就將事件攔截下來進行處理。
所以一共是三個函數,我們來總結下三個函數的功能:
dispatchTouchEvent方法用于事件的分發,Android中所有的事件都必須經過這個方法的分發,然后決定是自身消費當前事件還是繼續往下分發給子控件處理。返回true表示不繼續分發,事件沒有被消費。返回false則繼續往下分發,如果是ViewGroup則分發給onInterceptTouchEvent進行判斷是否攔截該事件。
onTouchEvent方法用于事件的處理,返回true表示消費處理當前事件,返回false則不處理,交給子控件進行繼續分發。
onInterceptTouchEvent是ViewGroup中才有的方法,View中沒有,它的作用是負責事件的攔截,返回true的時候表示攔截當前事件,不繼續往下分發,交給自身的onTouchEvent進行處理。返回false則不攔截,繼續往下傳。這是ViewGroup特有的方法,因為ViewGroup中可能還有子View,而在Android中View中是不能再包含子View的(iOS可以)。
流程簡述:
1.ViewGroup執行dispatchTouchEvent進行分發事件,可以進行攔截或者向下分發給子View。如果攔截事件則執行自己的onTouchEvent。
2.子View接受到事件執行dispatchTouchEvent,在這之中如果設置了Listener監聽器,則先執行onTouch方法,然后執行onTouchEvent方法。
然后事件被消費結束。
補充如果子View的Listener監聽器重寫的onTouch方法返回true,則不會繼續執行onTouchEvent方法,如果返回false則表示沒有消費結束,繼續執行onTouchEvent方法。
總結
- 底層將觸摸事件傳遞到上層的Activity,Activity再傳遞到ViewGroup,ViewGroup攔截或者不攔截,不攔截則傳遞分發到子View進行消費,如果這個事件一直沒被消費則自動被丟棄。