<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                相關文章? [Android View體系(一)視圖坐標系](http://blog.csdn.net/itachi85/article/details/50708391)? [Android View體系(二)實現View滑動的六種方法](http://blog.csdn.net/itachi85/article/details/50724558)? [Android View體系(三)屬性動畫](http://blog.csdn.net/itachi85/article/details/49835449)? [Android View體系(四)從源碼解析Scroller](http://blog.csdn.net/itachi85/article/details/50781845) ### 前言 三年前寫過事件分發機制的文章但是寫的不是很好,所以重新再寫一篇,關于事件分發機制的文章已經有很多,但是希望我這篇是最簡潔、最易懂的一篇。 ### 1.處理點擊事件的方法 #### View的層級 我們知道View的結構是樹形的結構,View可以放在ViewGroup中,這個ViewGroup也可以放到另一個ViewGroup中,這樣層層的嵌套就組成了View的層級。 #### 什么是點擊事件分發 當我們點擊屏幕,就產生了觸摸事件,這個事件被封裝成了一個類:MotionEvent。而當這個MotionEvent產生后,那么系統就會將這個MotionEvent傳遞給View的層級,MotionEvent在View的層級傳遞的過程就是點擊事件分發。 #### 點擊事件分發的重要方法 點擊事件有三個重要的方法它們分別是: * dispatchTouchEvent(MotionEvent ev):用來進行事件的分發 * onInterceptTouchEvent(MotionEvent ev):用來進行事件的攔截,在dispatchTouchEvent()中調用,需要注意的是View沒有提供該方法 * onTouchEvent(MotionEvent ev):用來處理點擊事件,在dispatchTouchEvent()方法中進行調用 為了了解這三個方法的關系,我們先來看看ViewGroup的dispatchTouchEvent()方法的部分源碼: ~~~ @Override public boolean dispatchTouchEvent(MotionEvent ev) { ...省略 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; } ...省略 return handled; } ~~~ 很明顯在dispatchTouchEvent()方法中調用了onInterceptTouchEvent()方法來判斷是否攔截事件,來看看onInterceptTouchEvent()方法: ~~~ public boolean onInterceptTouchEvent(MotionEvent ev) { return false; } ~~~ onInterceptTouchEvent()方法默認返回false,不進行攔截,接著來看看dispatchTouchEvent()方法剩余的部分源碼: ~~~ public boolean dispatchTouchEvent(MotionEvent ev) { ...省略 final View[] children = mChildren; for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex); // If there is a view that has accessibility focus we want it // to get the event first and if not handled we will perform a // normal dispatch. We may do a double iteration but this is // safer given the timeframe. if (childWithAccessibilityFocus != null) { if (childWithAccessibilityFocus != child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { // Child is already receiving touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); if (preorderedList != null) { // childIndex points into presorted list, find original index for (int j = 0; j < childrenCount; j++) { if (children[childIndex] == mChildren[j]) { mLastTouchDownIndex = j; break; } } } else { mLastTouchDownIndex = childIndex; } mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } // The accessibility focus didn't handle the event, so clear // the flag and do a normal dispatch to all children. ev.setTargetAccessibilityFocus(false); } ...省略 } ~~~ 我們看到了for循環,首先遍歷ViewGroup的子元素,判斷子元素是否能夠接收到點擊事件,如果子元素能夠接收到則交由子元素來處理。接下來看看37行的dispatchTransformedTouchEvent()方法中實現了什么: ~~~ private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; // Canceling motions is a special case. We don't need to perform any transformations // or filtering. The important part is the action, not the contents. final int oldAction = event.getAction(); if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { event.setAction(MotionEvent.ACTION_CANCEL); if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; } ...省略 } ~~~ 如果有子View則調用子View的dispatchTouchEvent(event)方法。如果ViewGroup沒有子View則調用super.dispatchTouchEvent(event),ViewGroup是繼承View的,我們再來看看View的dispatchTouchEvent(event): ~~~ public boolean dispatchTouchEvent(MotionEvent event) { ...省略 boolean result = false; if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; } } ...省略 return result; } ~~~ 我們看到如果OnTouchListener不為null并且onTouch()方法返回true,則表示事件被消費,就不會執行onTouchEvent(event),否則就會執行onTouchEvent(event)。再來看看onTouchEvent()方法的部分源碼: ~~~ public boolean onTouchEvent(MotionEvent event) { ...省略 final int action = event.getAction(); if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { switch (action) { case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0; if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { // take focus if we don't have it already and we should in // touch mode. boolean focusTaken = false; if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { // This is a tap, so remove the longpress check removeLongPressCallback(); // Only perform take click actions if we were in the pressed state if (!focusTaken) { // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start. if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { performClick(); } } } ...省略 } return true; } return false; } ~~~ 上面可以看到只要View的CLICKABLE和LONG_CLICKABLE一個為true,那么onTouchEvent就會返回true消耗這個事件。CLICKABLE和LONG_CLICKABLE代表View可以被點擊和長按點擊,可以通過View的setClickable和setLongClickable方法來設置,也可以通過View的setOnClickListenter和setOnLongClickListener來設置,他們會自動將View的設置為CLICKABLE和LONG_CLICKABLE。? 接著在ACTION_UP事件會調用performClick()方法: ~~~ public boolean performClick() { final boolean result; final ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); result = true; } else { result = false; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); return result; } ~~~ 如果View設置了點擊事件OnClickListener,那么它的onClick()方法就會被執行。 ### 2.點擊事件分發的傳遞規則 看到這里我們就可以知道點擊事件分發的這三個重要方法的關系,用偽代碼來簡單表示就是: ~~~ public boolean dispatchTouchEvent(MotionEvent ev) { boolean result=false; if(onInterceptTouchEvent(ev)){ result=super.onTouchEvent(ev); }else{ result=child.dispatchTouchEvent(ev); } return result; ~~~ #### 點擊事件由上而下的傳遞規則 當點擊事件產生后會由Activity來處理在傳遞給Window再傳遞給頂層的ViewGroup,一般在事件傳遞中只考慮ViewGroup的onInterceptTouchEvent()方法,因為一般情況我們不會去重寫dispatchTouchEvent()方法。? 對于根ViewGroup,點擊事件首先傳遞給它的dispatchTouchEvent()方法,如果該ViewGroup的onInterceptTouchEvent()方法返回true,則表示它要攔截這個事件,這個事件就會交給它的onTouchEvent()方法處理,如果onInterceptTouchEvent()方法返回false,則表示它不攔截這個事件,則交給它的子元素的dispatchTouchEvent()來處理,如此的反復下去。如果傳遞給最底層的View,View是沒有子View的,就會調用View的dispatchTouchEvent()方法,一般情況下最終會調用View的onTouchEvent()方法。 舉個現實的例子,就是我們的應用產生了重大的bug,這個bug首先會匯報給技術總監那: 技術總監(頂層ViewGroup)→技術經理(中層ViewGroup)→工程師(底層View)? 技術總監不攔截,把bug分給了技術經理,技術經理不攔截把bug分給了工程師,工程師沒有下屬只有自己處理了。? 事件由上而下傳遞返回值規則為:true,攔截,不繼續向下傳遞;false,不攔截,繼續向下傳遞。 #### 點擊事件由下而上的傳遞規則 點擊事件傳給最底層的View,如果他的onTouchEvent()方法返回true,則事件由最底層的View消耗并處理了,如果返回false則表示該View不做處理,則傳遞給父View的onTouchEvent()處理,如果父View的onTouchEvent()仍舊返回返回false,則繼續傳遞給改父View的父View處理,如此的反復下去。 再返回我們現實的例子,工程師發現這個bug太難搞不定(onTouchEvent()返回false),他只能交給上級技術經理處理,如果技術經理也搞不定(onTouchEvent()返回false),那就把bug傳給技術總監,技術總監一看bug很簡單就解決了(onTouchEvent()返回true)。 事件由下而上傳遞返回值規則為:true,處理了,不繼續向上傳遞;false,不處理,繼續向上傳遞。 #### 點擊事件傳遞時的其他問題 * 上面源碼我們看到:如果我們設置了OnTouchListener并且onTouch()方法返回true,則onTouchEvent()方法不會被調用,否則則會調用onTouchEvent()方法,可見OnTouchListener的優先級要比onTouchEvent()要高。在OnTouchEvent()方法中,如果當前設置了OnClickListener則會執行它的onClick()方法。 * View的OnTouchEvent()方法默認都會返回true,除非它是不可點擊的也就是CLICKABLE和LONG_CLICKABLE都為false。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看