Android中view的事件分發(fā)機制

1、View的事件分發(fā)涉及到3個核心的方法:

1.1、第一個是dispatchtouchevent,這個方法是用來進行事件分發(fā),如果事件能夠傳遞給當前 view, 那么此方法一定被調(diào)用,它的返回值受當前view的ontouchevent和下級view的 dispatchevent的影響,表示是否消耗當前事件;
1.2、第二個方法是oninterceptouchevent,這個方法在 dispatchtouchevent方法內(nèi)部調(diào)用,用來判斷是否攔截某個事件,如果當前view攔截了某個事件 ,那么在同一事件序列中 ,此方法不會 再次被調(diào)用,返回的結(jié)果表示是否攔截當前事件;
1.3、第三個方法是ontouchevent,這個方法也是在dispatchtouchevent方法中調(diào)用,用來處理事件,返回的結(jié)果表示是否消耗當前事件,如果不消耗,同一 事件序列中 ,當前view無法再次接 受到此事件序列中的后續(xù)事件。

三個方法的聯(lián)系可用一下偽代碼生動描述

public boolean dispatchTouchEvent(MotionEvent event) {
  boolean consume = false;
  if(onInterceptTouchEvent(ev)) {
    consume = onTouchEvent(ev);
  } else {
    consume = child.dispatchTouchEvent(ev);
  }
  return consume;
}

2、具體過程

具體來說,對于一個根 viewgroup來說,事件產(chǎn)生后,首先會傳遞給它,這時它的dispatchtouchevent方法會被調(diào)用,如果這個viewgroup的interceptedtouchevent方法返回為 true,就表示它要攔截當前事件,那么它的 ontouchevent方法會被調(diào)用,去處理這個事件 ;
如果這個viewgroup的oninterceptouchevent方法返回為 false,就表示它不攔截當前事件, 這時當前事件就會繼續(xù)傳遞給它的子view,接著子view的dispatchtouchevent方法就會被調(diào)用,如此往下,直至事件最終被處理。
當一個 view開始處理某個事件,如果它設(shè)置了ontouchlistener, 那么它的ontouch方法會被調(diào)用,如果ontouch返回為true,那么view的ontouchevent方法不會被調(diào)用,如果返回為false,當前view的ontouchevent方法會被調(diào)用,如果當前view還設(shè)置了onclicklistener,那么onclick也會被隨后調(diào)用。

3、事件分發(fā)的一些結(jié)論

(1)、若 ViewGroup 攔截了一個半路的事件(如MOVE),該事件將會被系統(tǒng)變成一個CANCEL事件 & 傳遞給之前處理該事件的子View;
該事件不會再傳遞給ViewGroup的onTouchEvent();
只有再到來的事件才會傳遞到ViewGroup的onTouchEvent()。
因為一旦當前事件變成取消事件,mFirstTouchTarget就會被置為null。
(2)、ViewGroup的onInterceptTouchEvent()對事件返回了false,但后續(xù)的事件(MOVE、UP)依然會傳遞給它的onInterceptTouchEvent() ;只有在onInterceptTouchEvent方法返回為true,此方法將不會被調(diào)用。
(3)、某個View一旦開始處理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent()返回了false),那么同一事件序列中的其他事件都不會再交給它處理,并且事件將重新交由它的父元素去處理,即父元素的onTouchEvent()會被調(diào)用。
(4)、ViewGroup默認不攔截事件。onInterceptTouchEvent()方法默認返回false。
(5)、View沒有onInterceptTouchEvent()方法,一旦有事件傳遞給它,那么它的onTouchEvent()方法就會被調(diào)用。
(6)、View的onTouchevent()默認都會消耗事件(返回true),除非它是不可點擊的(clickable和longClickable同時為false)。
(7)、onClick會發(fā)生的前提是當前View是可點擊的,并且它收到了down和up的事件。
(8)、當面對ACTION_DOWN事件時,ViewGroup總是會調(diào)用自己的onInterceptTouchEvent()方法來詢問自己是否要攔截該事件。

以下是安卓藝術(shù)探索沒說清楚的:

(9)、正常情況下,一個事件序列只能被一個View攔截并消耗。(異常情況對應(yīng)第1條)
(10)某個View一旦決定攔截,那么這一個事件序列都只能由它來處理(如果事件序列能夠傳遞給它的話,異常情況對應(yīng)第一條),并且它的onInterceptTouchEvent()不會再被調(diào)用。

(2)(3)證明代碼
 public boolean dispatchTouchEvent(MotionEvent ev) {
             ..............
            //mFirstTouchTarget是ViewGroup中處理事件(return true)的子View
            //如果沒有子View處理則mFirstTouchTarget=null,ViewGroup自己處理
       final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    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;
  }
}

當事件是ACTION_DOWN或者有子View去處理這個事件時,父view的onInterceptTouchEvent方法才會去調(diào)用,(ViewGroup在兩種情況下會去判斷是否攔截事件(ACTION_DOWN || mFirstTouchTarget != null)),反過來說就是: 當當前事件是ACTION_MOVE和ACTION_UP事件時,如果子View沒有處理down事件(mFirstTouchTarget==null),(第一種情況:是父view的onInterceptTouchEvent返回true,處理了事件;第一種情況:是子view沒有消耗down事件。不管是哪種情況)
ViewGroup的onInterceptTouchEvent不會再被調(diào)用,后續(xù)事件都會默認交給父view處理;子view也無法接收到后續(xù)事件。

1,view滑動總結(jié):http://www.itdecent.cn/p/61ad263c4a0e
2,view滑動沖突
2,Android事件分發(fā)機制 詳解攻略,您值得擁有:https://blog.csdn.net/carson_ho/article/details/54136311
3,事件分發(fā)機制全解析https://juejin.cn/post/6844903849723953166

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

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

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