Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android事件分發機制---學習筆記

Android事件分發機制---學習筆記

編輯:關於Android編程

學習到的知識

整個事件流向應該是從Activity—->ViewGroup—>View 從上往下調用dispatchTouchEvent方法
到View的時候,再由View—>ViewGroup—>Activity從下往上調用onTouchEvent方法。 android對事件分發的順序為:Activity–>PhoneWindow->DecorView->yourView; android控件對事件處理的優先級:onTouch>onTouchEvent>onClick dispatchTouchEvent==》分發事件 | onInterceptTouchEvent==》攔截事件 | onTouchEvent==》處理事件 整個View的事件轉發流程是:
View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent

在dispatchTouchEvent中會進行OnTouchListener的判斷,如果OnTouchListener不為null且返回true,則表示事件被消費,onTouchEvent不會被執行;否則執行onTouchEvent。

Activity的dispatchTouchEvent(MotionEvent ev)

    public boolean dispatchTouchEvent(MotionEvent ev) {  
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {  
                //該方法為空方法,直接忽略之    
                onUserInteraction();  
            }  
             //把事件ev交給phoneWindow來處理  
             if (getWindow().superDispatchTouchEvent(ev)) {  
                //表明整個事件到此結束,處理完畢  
                return true;  
            }  
            //說明該事件在View中沒有得到處理,由Activity自己處理  
            //至於怎麼處理博客後面後有說明  
            return onTouchEvent(ev);  
        }  

接著是superDispatchTouchEvent的方法

    @Override  
       public boolean superDispatchTouchEvent(MotionEvent event) {  
           return mDecor.superDispatchTouchEvent(event);  
       }  

mDecor.superDispatchTouchEvent傳給了DecorView

    public boolean dispatchTouchEvent(event){  
      //如果當前View對此事件攔截成功  
      if(this.onInterceptTouchEvent(event)){  
          //由當前View對此事件進行處理  
         //true 表示處理了該事件,false表示沒有處理該事件  
            return onTouchEvent(event);  
      }else{//沒有攔截成功  
           //交給子類來分發攔截處理  
            return child.dispatchTouchEvent(event);  
      }  
    }  

所以就有了這張圖片

\<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxjb2RlPjxjb2RlPlZpZXdHcm91cNPA1Layu7vhttTAub3Yo6zS8s6qy/u1xG9uSW50ZXJjZXB0VG91Y2hFdmVudChNb3Rpb25FdmVudCBldinKvNbVt7W72LXEysdmYWxzZaOh1eLR+URlY29yVmlld7bUtb3AtLXEysK8/k1vdGlvbkV2ZW50vs3Wu9PQt9a3orW919NWaWV3sqLTydfTVmlld7340NDAub3Yus20psDttMvKwrz+wcsuIFZpZXew/MCo1rG907zMs9DT2lZpZXe1xNfTwODS8s6qxuS4uMDgVmlld8O709BvbkludGVyY2VwdFRvdWNoRXZlbnS3vbeoo6zL+dLUw7u3qLbUysK8/r340NDAub3Yo6zI57n71eLW1lZpZXe78cihtb3By8rCvP6jrMTHw7S+zbvh1rTQ0G9uVG91Y2hFdmVudLe9t6ijqLWxyLvV4tKyysfT0Mz1vP61xKOs1eK49sewzOHM9bz+1Nq21M/Cw+ZvblRvdWNot723qNf308O1xMqxuvK74dPQy7XD96OpoaMgPC9jb2RlPjwvY29kZT4NCjxoNCBpZD0="view的dispatchtouchevent">View的dispatchTouchEvent

 public boolean dispatchTouchEvent(MotionEvent event) {  
        if (!onFilterTouchEventForSecurity(event)) {  
            return false;  
        }  
  //從這裡看出,onTouch方法是優先於onTouchEvnent方法的  
        //如果onTouch方法返回了true的話,那麼onTouchEvent方法就沒法執行  
        //相應的onClick方法也不會去執行  
        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
                mOnTouchListener.onTouch(this, event)) {  
               //如果這裡返回true的話,那麼也表明此次事件被處理了  
               return true;  
        }  
        //回調onTouchEvent方法  
        return onTouchEvent(event);  
    }  
setOnTouchListener
 public void setOnTouchListener(OnTouchListener l) {  
     mOnTouchListener = l;  
 }  

ViewGroup的dispatchTouchEvent

    public boolean dispatchTouchEvent(MotionEvent ev) {  
        final int action = ev.getAction();  
        final float xf = ev.getX();  
        final float yf = ev.getY();  
        final float scrolledXFloat = xf + mScrollX;  
        final float scrolledYFloat = yf + mScrollY;  
        final Rect frame = mTempRect;  
        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  
        if (action == MotionEvent.ACTION_DOWN) {  
            if (mMotionTarget != null) {  
                mMotionTarget = null;  
            }  
            if (disallowIntercept || !onInterceptTouchEvent(ev)) {  
                ev.setAction(MotionEvent.ACTION_DOWN);  
                final int scrolledXInt = (int) scrolledXFloat;  
                final int scrolledYInt = (int) scrolledYFloat;  
                final View[] children = mChildren;  
                final int count = mChildrenCount;  
                for (int i = count - 1; i >= 0; i--) {  
                    final View child = children[i];  
                    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE  
                            || child.getAnimation() != null) {  
                        child.getHitRect(frame);  
                        if (frame.contains(scrolledXInt, scrolledYInt)) {  
                            final float xc = scrolledXFloat - child.mLeft;  
                            final float yc = scrolledYFloat - child.mTop;  
                            ev.setLocation(xc, yc);  
                            child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
                            if (child.dispatchTouchEvent(ev))  {  
                                mMotionTarget = child;  
                                return true;  
                            }  
                        }  
                    }  
                }  
            }  
        }  
        boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||  
                (action == MotionEvent.ACTION_CANCEL);  
        if (isUpOrCancel) {  
            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;  
        }  
        final View target = mMotionTarget;  
        if (target == null) {  
            ev.setLocation(xf, yf);  
            if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {  
                ev.setAction(MotionEvent.ACTION_CANCEL);  
                mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
            }  
            return super.dispatchTouchEvent(ev);  
        }  
        if (!disallowIntercept && onInterceptTouchEvent(ev)) {  
            final float xc = scrolledXFloat - (float) target.mLeft;  
            final float yc = scrolledYFloat - (float) target.mTop;  
            mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
            ev.setAction(MotionEvent.ACTION_CANCEL);  
            ev.setLocation(xc, yc);  
            if (!target.dispatchTouchEvent(ev)) {  
            }  
            mMotionTarget = null;  
            return true;  
        }  
        if (isUpOrCancel) {  
            mMotionTarget = null;  
        }  
        final float xc = scrolledXFloat - (float) target.mLeft;  
        final float yc = scrolledYFloat - (float) target.mTop;  
        ev.setLocation(xc, yc);  
        if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {  
            ev.setAction(MotionEvent.ACTION_CANCEL);  
            target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
            mMotionTarget = null;  
        }  
        return target.dispatchTouchEvent(ev);  
    }  

onTouchEvent的源碼

    public boolean onTouchEvent(MotionEvent event) {  
        final int viewFlags = mViewFlags;  
        if ((viewFlags & ENABLED_MASK) == DISABLED) {  
            // A disabled view that is clickable still consumes the touch  
            // events, it just doesn't respond to them.  
            return (((viewFlags & CLICKABLE) == CLICKABLE ||  
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));  
        }  
        if (mTouchDelegate != null) {  
            if (mTouchDelegate.onTouchEvent(event)) {  
                return true;  
            }  
        }  
      if (((viewFlags & CLICKABLE) == CLICKABLE ||  
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  
            switch (event.getAction()) {  
                case MotionEvent.ACTION_UP://抬起事件才會執行onClick  
                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;  
                    if ((mPrivateFlags & PRESSED) != 0 || prepressed) {  
                      ..........  
                        if (!mHasPerformedLongPress) {  
                            .........  
                            if (!focusTaken) {  
                                 .........  
                                if (mPerformClick == null) {  
                                    mPerformClick = new PerformClick();  
                                }  
                //這也就說明了onTouchEvent優先於onClick執行,因為onClick方法就在onTouchEvent方法裡執行  
                //驗證了上面的結論  
                                if (!post(mPerformClick)) {  
                                     performClick();  
                                }  
                            }  
                        }  

                    ...........  
                    }  
                    break;              
            }  
            //此時返回了true,此View處理了該事件,事件不會再往下進行分發  
            return true;  
        }  
        return false;  
    }  

onTouch優先於onTouchEvent|onTouchEvent優先於onClick===>onClick在onTouchEvent裡面執行

返回true dispatchTouchEvent方法會結束執行 然後onClick就不會執行了
//因為該方法返回true,所以dispatchTouchEvent方法會結束執行  
//進而導致Button的onClick方法也沒機會執行  
               btn.setOnTouchListener(new OnTouchListener() {  

            @Override  
            public boolean onTouch(View v, MotionEvent event) {  
                return true;  
            }  
        });  

btn.setOnClickListener(new OnClickListener() {  

            @Override  
            public void onClick(View v) {             
                doClick();  

            }  
        });  

\
onInterceptTouchEvent負責對touch事件進行攔截,對於嵌套的view最先執行的是事件攔截方法的是最外層的那個view的onInterceptTouchEvent方法,然後依次執行子視圖的onInterceptTouchEvent,然後在執行子視圖的子視圖的事件攔截方法(當然在這裡假設所有嵌套視圖的onInterceptTouchEvent都會得到執行,讓每個視圖的onInterceptTouchEvent返回false即可)。參照上圖,所以onInterceptTouchEvent執行順序就是A—>B—>C—>D.也就是由父視圖到子視圖傳遞。總之,事件攔截機制是由父視圖開始發起對事件的攔截(出事了老子先上,兒子稍後)。參照上圖當手指觸摸事件時,父視圖A首先發起對該起事件的攔截,如果A攔截失敗,就交給它的子視圖B進行攔截;如果B攔截失敗就交給B的子視圖C再進行攔截..直到某一子視圖對該次事件攔截成功。 某一視圖攔截事件成功與否的判斷標識是onInterceptTouchEvent方法的返回值,當返回true的時候說明攔截成功,返回false的時候說明當前視圖對事件攔截失敗。 下面說說攔截成功的情況,假設C視圖對當前touch事件攔截成功。攔截成功意味著此次事件不會再傳遞到D視圖了。所以此時的D視圖的onInterceptTouchEvent就得不到運行(事件沒法到達了,還攔截誰呢?)。事件攔截成功後,緊接著就會對事件進行處理,處理的方法教給onTouchEvent方法處理。此時C視圖攔截成功,那麼緊接著就會執行C視圖的onTouchEvent方法,這是不是就意味著當前touch事件是由C視圖的onTouchEvent方法來處理的呢?這要由C視圖的onTouchEvent方法的返回值來決定。當C視圖的onTouchEvent返回true的時候,當前事件就由C全權處理,處理的當然是事件的各種action,什麼MotionEvent.ACTION_MOVE,ACTION_UP都交給了C的onTouchEvent方法進行處理。所以此時就可以在C的onTouchEvent方法中進行switch(event.getAction)判斷執行相關邏輯了。如果返回的false,說明C視圖對此事件不做處理或者處理不了,怎麼辦呢?兒子不行老爸來,於是事件就交到了B視圖的onTouchEvent方法中。同樣B對此事件處理與否還是看B的onTouchEvent返回值,具體的解釋就跟C一樣了,不復多言。 在A B C D的onInterceptTouchEvent和onTouchEvent都返回false的情況下,方法執行的順序依次為A.onInterceptTouchEvent–>B.onInterceptTouchEvent–>C.onInterceptTouchEvent–>D.touchEvent(最深的子視圖沒重寫onInterceptTouchEvent)–>C.touchEvent–>B.touchEvent–>A.touchEvent.也就是說攔截事件是父視圖優先有子視圖進行攔截,處理事件是子視圖優先父視圖進行處理。
onInterceptTouchEvent負責對事件進行攔截,攔截成功後交給最先遇到onTouchEvent返回true的那個view進行處理。

Android中touch事件的傳遞,絕對是先傳遞到ViewGroup,再傳遞到View的

\

當你點擊了某個控件,首先會去調用該控件所在布局的dispatchTouchEvent方法,然後在布局的dispatchTouchEvent方法中找到被點擊的相應控件,再去調用該控件的dispatchTouchEvent方法。如果我們點擊了MyLayout中的按鈕,會先去調用MyLayout的dispatchTouchEvent方法,可是你會發現MyLayout中並沒有這個方法。那就再到它的父類LinearLayout中找一找,發現也沒有這個方法。那只好繼續再找LinearLayout的父類ViewGroup,你終於在ViewGroup中看到了這個方法,按鈕的dispatchTouchEvent方法就是在這裡調用的

在ViewGroup中可以通過onInterceptTouchEvent方法對事件傳遞進行攔截,onInterceptTouchEvent方法返回true代表不允許事件繼續向子View傳遞,返回false代表不對事件進行攔截,默認返回false。 子View中如果將傳遞的事件消費掉,ViewGroup中將無法接收到任何事件。

 

以下內容來自圖解 Android 事件分發機制

dispatchTouchEvent和onTouchEvent一旦return true,事件就停止傳遞了 dispatchTouchEvent 和 onTouchEvent return false的時候事件都回傳給父控件的onTouchEvent處理。
對於dispatchTouchEvent 返回 false 的含義應該是:事件停止往子View傳遞和分發同時開始往父控件回溯(父控件的onTouchEvent開始從下往上回傳直到某個onTouchEvent return true),事件分發機制就像遞歸,return false 的意義就是遞歸停止然後開始回溯。 對於onTouchEvent return false 就比較簡單了,它就是不消費事件,並讓事件繼續往父控件的方向從下往上流動。

dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
ViewGroup 和View的這些方法的默認實現就是會讓整個事件安裝U型完整走完,所以 return super.xxxxxx() 就會讓事件依照U型的方向的完整走完整個事件流動路徑),中間不做任何改動,不回溯、不終止,每個環節都走到。如果看到方法return super.xxxxx() 那麼事件的下一個流向就是走U型下一個目標

onInterceptTouchEvent 的作用

默認是不會去攔截的,因為子View也需要這個事件,所以onInterceptTouchEvent攔截器return super.onInterceptTouchEvent()和return false是一樣的,是不會攔截的,事件會繼續往子View的dispatchTouchEvent傳遞。
對於dispatchTouchEvent,onTouchEvent,return true是終結事件傳遞。return false 是回溯到父View的onTouchEvent方法。 ViewGroup 想把自己分發給自己的onTouchEvent,需要攔截器onInterceptTouchEvent方法return true 把事件攔截下來。 ViewGroup的攔截器onInterceptTouchEvent 默認是不攔截的,所以return super.onInterceptTouchEvent()=return false; View 沒有攔截器,為了讓View可以把事件分發給自己的onTouchEvent,View的dispatchTouchEvent默認實現(super)就是把事件分發給自己的onTouchEvent。

ViewGroup和View 的dispatchTouchEvent 是做事件分發,那麼這個事件可能分發出去的四個目標

注:------> 後面代表事件目標需要怎麼做。
1、 自己消費,終結傳遞。------->return true ;
2、 給自己的onTouchEvent處理-------> 調用super.dispatchTouchEvent()系統默認會去調用 onInterceptTouchEvent,在onInterceptTouchEvent return true就會去把事件分給自己的onTouchEvent處理。
3、 傳給子View------>調用super.dispatchTouchEvent()默認實現會去調用 onInterceptTouchEvent 在onInterceptTouchEvent return false,就會把事件傳給子類。
4、 不傳給子View,事件終止往下傳遞,事件開始回溯,從父View的onTouchEvent開始事件從下到上回歸執行每個控件的onTouchEvent------->return false;
注: 由於View沒有子View所以不需要onInterceptTouchEvent 來控件是否把事件傳遞給子View還是攔截,所以View的事件分發調用super.dispatchTouchEvent()的時候默認把事件傳給自己的onTouchEvent處理(相當於攔截),對比ViewGroup的dispatchTouchEvent 事件分發,View的事件分發沒有上面提到的4個目標的第3點。

ViewGroup和View的onTouchEvent方法是做事件處理的,那麼這個事件只能有兩個處理方式:

1、自己消費掉,事件終結,不再傳給誰----->return true;
2、繼續從下往上傳,不消費事件,讓父View也能收到到這個事件----->return false;View的默認實現是不消費的。所以super==false。

ViewGroup的onInterceptTouchEvent方法對於事件有兩種情況:

1、攔截下來,給自己的onTouchEvent處理--->return true;
2、不攔截,把事件往下傳給子View---->return false,ViewGroup默認是不攔截的,所以super==false;
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved