偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Android事件分發(fā)機制源碼和實例解析

移動開發(fā) Android
大體的分發(fā)過程為:首先傳遞到 Activity,然后傳給了 Activity 依附的 Window,接著由 Window 傳給視圖的頂層 View 也就是 DecorView,最后由 DecorView 向整個 ViewTree 分發(fā)。分發(fā)還會有回溯的過程。最后還會回到 Activity 的調(diào)用中。

1.事件分發(fā)過程的理解

1.1. 概述

1.2. 主要方法

1.3. 核心行為

1.4. 特殊情況

2.案例分析

2.1. 案例1:均不消費 down 事件

2.2. 案例2:View0 消費 down 事件

2.3. 案例3:ViewGroup2nd 消費 down 事件

3.down 事件分發(fā)圖

1. 事件分發(fā)過程的理解

1.1. 概述

事件主要有 down(MotionEvent.ACTION_DOWN),move(MotionEvent.ACTION_MOVE),up(MotionEvent.ACTION_UP)。

基本上的手勢均由 down 事件為起點,up 事件為終點,中間可能會有一定數(shù)量的move 事件。這三種事件是大部分手勢動作的基礎(chǔ)。

事件和相關(guān)信息(比如坐標(biāo))封裝成 MotionEvent。

大體的分發(fā)過程為:首先傳遞到 Activity,然后傳給了 Activity 依附的 Window,接著由 Window 傳給視圖的頂層 View 也就是 DecorView,***由 DecorView 向整個 ViewTree 分發(fā)。分發(fā)還會有回溯的過程。***還會回到 Activity 的調(diào)用中。

Activity 的分發(fā)事件源碼

  1. public boolean dispatchTouchEvent(MotionEvent ev) {    if (ev.getAction() == MotionEvent.ACTION_DOWN) { 
  2.         onUserInteraction(); 
  3.     }    if (getWindow().superDispatchTouchEvent(ev)) {        return true
  4.     }    return onTouchEvent(ev); 

 

在 getWindow().superDispathTouchEvent 就是用來分發(fā)事件到 DecorView 中。如果整個 ViewTree 沒有消費事件,會調(diào)用 Activity 的 onTouchEvent。

1.2. 主要方法

1.2.1. 概覽

主要涉及到的 View 或 ViewGroup 的方法有:

dispatchTouchEvent,該方法封裝了事件分發(fā)的整個過程。是事件分發(fā)的 調(diào)度者 和 指揮官 。的核心過程均在該方法中。下面的 onInterceptTouchEvent 和 onTouchEvent 的回調(diào)的調(diào)用就在該方法體中。是否傳遞事件到

onInterceptTouchEvent 和 onTouchEvent 由 dispatchTouchEvent 決定。

onInterceptTouchEvent,該方法決定了是否攔截事件。只有 ViewGroup 有該回調(diào)。返回 true 表示攔截,返回 false 表示不攔截。自定義 View 的時候,可以重載該方法,通過一些特定的邏輯來決定是否攔截事件。如果攔截,接下來會調(diào)用該 ViewGroup 的 onTouchEvent 來處理事件。

onTouchEvent,該方法處理了事件,并決定是否繼續(xù)消費后續(xù)事件。該方法調(diào)用的前置條件:

  • 該 View 攔截了事件
  • 子 View 都不消費事件
  • 沒有子 View

該方法正式處理 MotionEvent。返回 true 表示消費,返回 false 不消費。如果消費,接下來的事件還會傳遞到該 View 的 dispatchTouchEvent 中;如果不消費,后面的事件不會再傳過來。

onTouchListener 的 onTouch 回調(diào),和 onTouchEvent 一樣,優(yōu)先級比 onTouchEvent 高,如果有設(shè)置該監(jiān)聽,并且 onTouch 返回 true,就不會再調(diào)用 onTouchEvent 了。如果返回 false,事件還是會傳遞到 onTouchEvent 中。

1.2.2. dispatchTouchEvent 方法中的一些細(xì)節(jié)處理:

大部分手勢的起點為 down 事件,dispatchTouchEvent 如果收到 down 事件,會重新設(shè)置一些變量和標(biāo)記

重置變量和標(biāo)記的源碼

  1. // Handle an initial down.if (actionMasked == MotionEvent.ACTION_DOWN) {    // Throw away all previous state when starting a new touch gesture. 
  2.     // The framework may have dropped the up or cancel event for the previous gesture 
  3.     // due to an app switch, ANR, or some other state change. 
  4.     cancelAndClearTouchTargets(ev); 
  5.     resetTouchState(); 

 

實際的源碼中,ViewGroup 繼承于 View。 當(dāng)子 View 不消費事件或者 ViewGroup 攔截了事件會傳空值到 dispatchTransformedTouchEvent 中,內(nèi)部會調(diào)用 super.dispatchTouchEvent,最終把事件傳給 onTouchEvent 進行處理。

dispatchTransformedTouchEvent 關(guān)鍵部分

 

  1. // Perform any necessary transformations and dispatch.if (child == null) { 
  2.     handled = super.dispatchTouchEvent(transformedEvent); 
  3. else {    final float offsetX = mScrollX - child.mLeft;    final float offsetY = mScrollY - child.mTop; 
  4.     transformedEvent.offsetLocation(offsetX, offsetY);    if (! child.hasIdentityMatrix()) { 
  5.         transformedEvent.transform(child.getInverseMatrix()); 
  6.     } 
  7.  
  8.     handled = child.dispatchTouchEvent(transformedEvent); 

 

也就是 dispatchTransformTouchEvent 完成了分發(fā)的***過程:

a. 傳入的 child 不為空,轉(zhuǎn)化坐標(biāo)為 child 的坐標(biāo)系,調(diào)用 child.dispatchTouchEvent向 child 分發(fā)事件

b. 傳入的 child 為空,調(diào)用 super.dispatchTouchEvent 分發(fā)事件到 onTouchEvent 中

1.2.3 方法的主要關(guān)系

對于一個 ViewGroup 來說,幾個重要方法的關(guān)系如下

幾個重要方法關(guān)系偽代碼

  1. public boolean dispatchTouchEvent(MotionEvent e) {    boolean consumed = false;    if (onInterceptTouchEvent(e)) { 
  2.         consumed = onTouchEvent(e); 
  3.     } else {        for (View view: childs) { 
  4.             consumed = view.dispatchTouchEvent(e);            if (consumed) {                break; 
  5.             } 
  6.         }        if (!consumed) { 
  7.             consumed = onTouchEvent(e); 
  8.         } 
  9.     }     
  10.     return consumed; 

 

這是事件分發(fā)過程的簡單描述,具體遠比這復(fù)雜的多。

1.3. 核心行為

View 或 ViewGroup 有兩個核心的行為:攔截(intercept) 和 消費(consume)。這兩者是相互獨立的,攔截不一定消費。是否要攔截看 onIntercepTouchEvent。是否要消費看 onTouchEvent。

注意:是否攔截還有其他因素影響。如果不是 down 事件,并且 mFirstTouchTarget 為空值,就會直接攔截事件。

在 dispatchTouchEvent 中有這樣的代碼

攔截的關(guān)鍵源碼

  1. // Check for interception.final boolean intercepted;if (actionMasked == MotionEvent.ACTION_DOWN 
  2.          || mFirstTouchTarget != null) {       final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;    if (!disallowIntercept) { 
  3.         intercepted = onInterceptTouchEvent(ev); 
  4.         ev.setAction(action); // restore action in case it was changed 
  5.     } else { 
  6.         intercepted = false
  7.        } 
  8. else {    // There are no touch targets and this action is not an initial down 
  9.     // so this view group continues to intercept touches. 
  10.     intercepted = true

 

從上面的源碼可以看出,在不是 down 事件,并且 mFirstTouchTarget 為空的情況下,不會走 onInterceptTouchEvent 而是直接攔截。如果滿足了,還會看 FLAG_DISALLOW_INTERCEPT 標(biāo)記,如果不允許攔截(disallowIntercept 為 true),也不會走onInterceptTouchEvent,直接標(biāo)記不攔截。

處理調(diào)用 onTouchEvent 的源碼

  1. boolean result = false
  2.  
  3. ... 
  4.  
  5. ListenerInfo li = mListenerInfo;if (li != null && li.mOnTouchListener != null 
  6.            && (mViewFlags & ENABLED_MASK) == ENABLED 
  7.         && li.mOnTouchListener.onTouch(this, event)) { 
  8.     result = true
  9. }if (!result && onTouchEvent(event)) { 
  10.     result = true

 

可以看出,在該 View 為 ENABLE 的狀態(tài)并且有 mTouchListener,會先調(diào)用 onTouch。在onTouch 返回 false 時才會繼續(xù)調(diào)用 onTouchEvent。

onTouch 或者 onTouchEvent 的處理結(jié)果有:

  • 返回 true,會繼續(xù)消費后續(xù)事件。意味著,后面的事件將會繼續(xù)傳遞到該 View 的 dispatchTouchEvent 方法中進行調(diào)度。父 View 會為該 View 創(chuàng)建一個 TouchTarget 實例加入鏈表中,鏈表的***項為 mFirstTouchTarget。后續(xù)的 move 和 up 事件會直接交給該 View 的 dispatchTouchEvent。
  • 返回 false,不再消費后續(xù)事件。意味著,后面的事件將會被父 View 攔截,而不再傳遞下來。

1.4. 特殊情況

比較特殊的情況有,子 View 可以使用 requestDisallowInterceptTouchEvent 影響去父 View 的分發(fā),可以決定父 View 是否要調(diào)用 onInterceptTouchEvent 。比如,requestDisallowInterceptTouchEvent(true),父 View 就不用調(diào)用 onInterceptTouchEvent 來判斷攔截,而就是不攔截。

該方法可以用來解決手勢沖突。比如子 View 先消費了事件,但是后面父 View 也滿足了手勢觸發(fā)的條件而攔截事件,導(dǎo)致子 View 手勢執(zhí)行一半后無法繼續(xù)響應(yīng)??梢允褂?requestDisallowInterceptTouchEvent(true),這樣后面的事件,父 View 不會走 onInterceptTouchEvent 回調(diào)來判斷是否要攔截事件,而是直接把事件繼續(xù)傳下來。

2. 案例分析

下面舉三個簡單的例子,三個類 ViewGroup1st,ViewGroup2nd 和 View0,層級關(guān)系為

  1. <ViewGroup1st> 
  2.     <ViewGroup2nd> 
  3.         <View0 /> 
  4.     </ViewGroup2nd></ViewGroup1st> 

 

這三個類有兩層 ViewGroup,***層為 View,這幾個例子主要理解 消費 行為,所以不做事件的攔截。

2.1. 案例1:均不消費 down 事件

在觸摸屏幕中 View0 的區(qū)域后,輸出 log 信息如下

 

  1. 12-30 14:06:03.694 31323-31323/lyn.demo D/ViewGroup1st: dispatchTouchEvent before12-30 14:06:03.694 31323-31323/lyn.demo D/ViewGroup1st: onInterceptTouchEvent return:false12-30 14:06:03.694 31323-31323/lyn.demo D/ViewGroup2nd: dispatchTouchEvent before12-30 14:06:03.694 31323-31323/lyn.demo D/ViewGroup2nd: onInterceptTouchEvent return:false12-30 14:06:03.694 31323-31323/lyn.demo D/View0: dispatchTouchEvent before12-30 14:06:03.694 31323-31323/lyn.demo D/View0: onTouchEvent return:false12-30 14:06:03.694 31323-31323/lyn.demo D/View0: dispatchTouchEvent return:false12-30 14:06:03.694 31323-31323/lyn.demo D/ViewGroup2nd: onTouchEvent return:false12-30 14:06:03.694 31323-31323/lyn.demo D/ViewGroup2nd: dispatchTouchEvent return:false12-30 14:06:03.694 31323-31323/lyn.demo D/ViewGroup1st: onTouchEvent return:false12-30 14:06:03.694 31323-31323/lyn.demo D/ViewGroup1st: dispatchTouchEvent return:false 

當(dāng) down 事件從 DecorView 開始了分發(fā)過程:

ViewGroup1st 收到事件,執(zhí)行 onInterceptTouchEvent 返回 false,不攔截,于是調(diào)用 ViewGroup2nd 的 dispatchTouchEvent 向 ViewGroup2nd分發(fā)。

ViewGroup2nd 收到事件,dispatchTouchEvent 重復(fù) ViewGroup1st 的分發(fā)策略。因為都不攔截,所以調(diào)用了 View0 的 dispatchTouchEvent。

View0 收到事件,而 View0 不是 ViewGroup 類型,所以把事件直接交給了 onTouchEvent。

View0 不消費事件,onTouchEvent 返回 false,dispatchTouchEvent 方法因此也返回 false。

ViewGroup2nd 因為 View0 的 dispatchTouchEvent 返回 false,確定了子類不消費事件,于是把事件傳遞給 onTouchEvent。但本身也不消費事件,所以 onTouchEvent 也返回 false,繼續(xù)把事件上拋到 ViewGroup1st。

ViewGroup1st 重復(fù)了 ViewGroup2nd 的過程。

隨后,move 事件不會再往下傳了,而是直接被 Activity 攔截。

2.2. 案例2:View0 消費 down 事件

首先是 down 事件的傳遞,log 如下

  1. 12-30 14:14:09.384 7350-7350/lyn.demo D/ViewGroup1st: dispatchTouchEvent before12-30 14:14:09.384 7350-7350/lyn.demo D/ViewGroup1st: onInterceptTouchEvent return:false12-30 14:14:09.384 7350-7350/lyn.demo D/ViewGroup2nd: dispatchTouchEvent before12-30 14:14:09.384 7350-7350/lyn.demo D/ViewGroup2nd: onInterceptTouchEvent return:false12-30 14:14:09.384 7350-7350/lyn.demo D/View0: dispatchTouchEvent before12-30 14:14:09.384 7350-7350/lyn.demo D/View0: onTouchEvent return:true12-30 14:14:09.384 7350-7350/lyn.demo D/View0: dispatchTouchEvent return:true12-30 14:14:09.384 7350-7350/lyn.demo D/ViewGroup2nd: dispatchTouchEvent return:true12-30 14:14:09.384 7350-7350/lyn.demo D/ViewGroup1st: dispatchTouchEvent return:true 

ViewGroup1st 和 ViewGroup2st 的傳遞和案例1一樣。區(qū)別在于 View0 onTouchEvent 返回 true 消費后續(xù)事件后,View0 的 dispatchTouchEvent 也返回 true,ViewGroup2nd 和 ViewGroup1st 不執(zhí)行 onTouchEvent 也直接返回 true

然后稍微移動一下手指,move 事件往下傳遞

  1. 12-30 14:14:09.484 7350-7350/lyn.demo D/ViewGroup1st: dispatchTouchEvent before12-30 14:14:09.484 7350-7350/lyn.demo D/ViewGroup1st: onInterceptTouchEvent return:false12-30 14:14:09.484 7350-7350/lyn.demo D/ViewGroup2nd: dispatchTouchEvent before12-30 14:14:09.484 7350-7350/lyn.demo D/ViewGroup2nd: onInterceptTouchEvent return:false12-30 14:14:09.484 7350-7350/lyn.demo D/View0: dispatchTouchEvent before12-30 14:14:09.484 7350-7350/lyn.demo D/View0: onTouchEvent return:true12-30 14:14:09.484 7350-7350/lyn.demo D/View0: dispatchTouchEvent return:true12-30 14:14:09.484 7350-7350/lyn.demo D/ViewGroup2nd: dispatchTouchEvent return:true12-30 14:14:09.484 7350-7350/lyn.demo D/ViewGroup1st: dispatchTouchEvent return:true 

過程和 down 事件的傳遞一樣。因為同樣會經(jīng)過 ViewGroup2nd 的 onInterceptTouchEvent,如果這時候 ViewGroup2nd 有攔截行為,move 事件就不會傳到 View0 了。要避免這種情況發(fā)生,需要調(diào)用 View0 的requestDisallowInterceptTouchEvent,可見 1.4 部分。

2.3. 案例3:ViewGroup2nd 消費 down 事件

首先是 down 事件的傳遞,log 如下

  1. 12-30 14:25:30.074 18848-18848/lyn.demo D/ViewGroup1st: dispatchTouchEvent before12-30 14:25:30.074 18848-18848/lyn.demo D/ViewGroup1st: onInterceptTouchEvent return:false12-30 14:25:30.084 18848-18848/lyn.demo D/ViewGroup2nd: dispatchTouchEvent before12-30 14:25:30.084 18848-18848/lyn.demo D/ViewGroup2nd: onInterceptTouchEvent return:false12-30 14:25:30.084 18848-18848/lyn.demo D/View0: dispatchTouchEvent before12-30 14:25:30.084 18848-18848/lyn.demo D/View0: onTouchEvent return:false12-30 14:25:30.084 18848-18848/lyn.demo D/View0: dispatchTouchEvent return:false12-30 14:25:30.084 18848-18848/lyn.demo D/ViewGroup2nd: onTouchEvent return:true12-30 14:25:30.084 18848-18848/lyn.demo D/ViewGroup2nd: dispatchTouchEvent return:true12-30 14:25:30.084 18848-18848/lyn.demo D/ViewGroup1st: dispatchTouchEvent return:true 

由于 View0 不消費事件,dispatchTouchEvent 返回 false,所以執(zhí)行了 ViewGroup2nd 的 onTouchEvent 方法。

ViewGroup2nd 消費事件,onTouchEvent 返回 true,之后 ViewGroup2nd 和 ViewGroup1st 的 dispatchTouchEvent 均返回 true。

動一下手指,move 事件接著傳

  1. 2-30 14:25:30.174 18848-18848/lyn.demo D/ViewGroup1st: dispatchTouchEvent before12-30 14:25:30.174 18848-18848/lyn.demo D/ViewGroup1st: onInterceptTouchEvent return:false12-30 14:25:30.174 18848-18848/lyn.demo D/ViewGroup2nd: dispatchTouchEvent before12-30 14:25:30.174 18848-18848/lyn.demo D/ViewGroup2nd: onTouchEvent return:true12-30 14:25:30.174 18848-18848/lyn.demo D/ViewGroup2nd: dispatchTouchEvent return:true12-30 14:25:30.174 18848-18848/lyn.demo D/ViewGroup1st: dispatchTouchEvent return:true 

這時候,ViewGroup2nd 直接攔截了 move 事件,不再經(jīng)過 onInterceptTouchEvent,也不再向 View0 分發(fā),而是直接調(diào)用 onTouchEvent 進行處理。

3. down 事件分發(fā)圖

在每個 View 都不攔截 down 事件的情況下,down 事件是這樣傳遞的

 

責(zé)任編輯:龐桂玉 來源: 安卓開發(fā)精選
相關(guān)推薦

2021-08-17 13:41:11

AndroidView事件

2023-10-08 08:23:44

Android事件邏輯

2010-08-06 10:03:42

Flex事件

2016-12-08 10:19:18

Android事件分發(fā)機制

2013-04-24 11:15:56

Android開發(fā)Touch事件傳遞機制

2017-03-14 13:51:23

AndroidView事件分發(fā)和處理

2017-07-20 16:55:56

Android事件響應(yīng)View源碼分析

2024-07-01 08:27:05

KeyAndroid按鍵事件

2021-09-09 06:55:43

AndroidViewDragHel原理

2010-08-06 10:24:56

Flex事件分發(fā)

2010-08-13 14:05:24

Flex事件機制

2015-10-26 09:25:42

2009-09-03 16:27:57

ASP.NET回車事件

2010-07-29 10:40:12

2011-06-23 14:05:32

Qt 事件機制

2009-09-03 16:38:49

C#回車鍵事件

2011-06-23 14:40:13

Qt 信號

2013-01-10 14:54:48

Android開發(fā)組件Intent

2010-08-04 14:02:08

Flex事件機制

2011-09-07 14:01:41

Android Wid實例
點贊
收藏

51CTO技術(shù)棧公眾號