# 安卓事件傳遞機制
> 參考文章:[https://www.jianshu.com/p/238d1b753e64?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation](https://www.jianshu.com/p/238d1b753e64?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation "https://www.jianshu.com/p/238d1b753e64?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation")
[](https://www.jianshu.com/p/238d1b753e64?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation)

> DecorView屬于View體系,當它調用super.dispatchTouchEvent()方法的時候,實際上是調用ViewGroup的dispatchTouchEvent()函數。
```
//code View.java
public boolean dispatchTouchEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
if (onFilterTouchEventForSecurity(event)) {
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) {
return true;
}
if (onTouchEvent(event)) {
return true;
}
}
...
return false;
}
```
> **在dispatchTouch()函數中,大致分成以下幾個過程:**
>
> 1\. 函數會優先調用onFilterTouchEventForSecurity()方法對事件進行攔截。這個函數是在特定的界面模糊不可點擊的情況下使用,一般我們不用理會。
>
> 2\. View會獲取OnTouchListener回調接口,并調用回調接口來處理事件。
>
> 3\. 當回調接口不予處理的時候,將調用自己的onTouch()回調來處理
>
> **所以對于View類來說,它的調用過程是:** dispatchTouch( )->OnTouchListener.onTouch( )->onTouch( )
```
//code ViewGroup.java
...
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
...
```
> 在剛開始執行dispatchTouch的時候,如果當前的事件是一個down事件,ViewGroup會清掉之前所保存的按下狀態。調用的方法是cancelAndClearTouchTargets和resetTouchState。比如當前按鈕處于按下狀態,因為某種原因沒有正常反彈,通過cancelAndClearTouchTargets就可以告訴按鈕當前狀態重置了,可以回彈了。之后,會執行ViewGroup的核心方法:
```
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
//判斷是否可以執行onInterceptTouchEvent
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;//判斷是否有必要執行onInterceptTouchEvent
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
```
> 這段代碼的核心,在于是否調用onInterceptTouchEvent回調方法。一旦onInterceptTouchEvent方法返回true,意味著這個事件需要由當前ViewGroup來處理,也就是把ViewGroup當成View來看,走上述的View事件處理流程,在此不再贅述。那么什么時候調用這個回調方法呢?
**ViewGroup的事件處理過程:**
> dispatchTouch() -> onInterceptTouchEvent -> (View流程) OnTouchListener.onTouch( ) -> onTouch()
### 結論:
* **當ViewGroup決定攔截事件后,后續事件將默認交給它處理并且不會再調用onInterceptTouchEvent方法來判斷是否攔截。子View可以通過設置FLAG_DISALLOW_INTERCEPT標志位來不讓ViewGroup攔截除ACTION_DOWN以外的事件。**
* **所以我們知道了onInterceptTouchEvent并非每次都會被調用。如果要處理所有的點擊事件那么需要選擇dispatchTouchEvent方法,而FLAG_DISALLOW_INTERCEPT標志位可以幫助我們去有效的處理滑動沖突**
* Android 事件分發總是遵循 Activity => ViewGroup => View 的傳遞順序;
* onTouch()?執行總優先于?onClick()
> 以下圖片來源:[https://www.jianshu.com/p/38015afcdb58](https://www.jianshu.com/p/38015afcdb58 "https://www.jianshu.com/p/38015afcdb58")
**Activity 的事件分發示意圖**

**ViewGroup 事件分發示意圖**

**View 的事件分發示意圖**

**事件分發工作流程總結**
