<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                原文參考[這里](http://blog.csdn.net/zizidemenghanxiao/article/details/50184295) 頂級View對點擊事件的分發過程: ~~~ /** * {@inheritDoc} */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; /* * 當新的一輪點擊到來的時候,從ACTION_DOWN開始的,做一些初始化的工作: * */ // Handle an initial down. 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. /* * 至少我知道在這個函數中最終將mFirstTouchTarget設為null。 * mFirstTouchTarget代表的就是一個事件序列中第一個攔截的對象, * 所以這里需要重置。 * */ cancelAndClearTouchTargets(ev); /* * 如果事件是ACTION_DOWN, * ViewGroup就會在resetTouchState中重置下面的FLAG_DISALLOW_INTERCEPT標志位。 * 重置的方式是這樣的:mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; * */ resetTouchState(); } // Check for interception. /* * 這個標識很重要,因為它一旦被標志位true,意味著下面的各種if語句都進不去了, * 意味著本ViewGroup攔截了該事件,并且后續的事件序列直接由該ViewGroup處理, * 而不是進入各種if中判斷是否需要攔截。 * */ final boolean intercepted;// 攔截標識 /* * 這個if中需要滿足兩個條件: * (1)actionMasked == MotionEvent.ACTION_DOWN: * 該事件是否為點擊下按事件時成立,就是說新的一輪事件到來 * (2)mFirstTouchTarget != null: * 當ViewGroup不攔截事件并將事件交給子元素處理時,成立,mFirstTouchTarget指向這個子元素。 * 而且在ViewGroup中,默認onInterceptTouchEvent返回false,它是不攔截任何事件的, * 但是在LinearLayout中可能就會攔截啊,可以改寫啊。 * 而且,當第二個條件成立時,此時發生的事件序列就是ACTION_MOVE或者ACTION_UP,都會進入到這個if語句中。 * */ /* * 所以說呢,當子元素成功攔截了事件或者下按事件發生的時候就會進入if語句。 * 所以說呢,如果子元素沒有處理,并且是move和up發生的時候就無法進入該if語句。 * 但為什么這樣設定呢,因為如果子元素沒有處理的話,事件序列中的其他事件就會直接由ViewGroup來處理了, * 不需要來這里來判斷一下到底要不要攔截事件了。那如果是move和up也是同樣的,不需要來這里來判斷要不要攔截事件。 * */ /* * 也就相當于說,一個事件,第一次因為ACTION_DOWN進入這里,然后ViewGroup判斷是否來攔截。 * 之后在子元素成功處理后,因為子元素是可以通過FLAG_DISALLOW_INTERCEPT標志位來干預父元素的事件分發過程,所以又來這里來要看是否攔截。 * */ /* * 為什么總說一旦父元素攔截ACTION_DOWN以后其他的事件序列就只能由父元素來處理呢? * 是因為如果父元素攔截了ACTION_DOWN,那么mFirstTouchTarget == null * 當ACTION_MOVE和ACTION_UP到來的時候,這條if語句就不會進入了, * 然后intercepted = true;表示事件序列由父元素全攔截了。 * */ if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { /* * 通常事件傳遞過程是由外向內的, * 但是通過 requestDisallowInterceptTouchEvent方法可以在子元素中干預父元素的事件分發過程, * 不過ACTION_DOWN事件除外。 * 干預表現在子元素已經攔截了事件, * 但是可以通過requestDisallowInterceptTouchEvent來控制 * ACTION_MOVE和ACTION_UP能不能夠進入到這里來。 * */ /* * FLAG_DISALLOW_INTERCEPT一旦設置后,ViewGroup將無法攔截處理ACTION_DOWN以外的其他點擊事件了。 * 因為在事件分發時,ACTION_DOWN會重置FLAG_DISALLOW_INTERCEPT標志位,表示另一次事件開始。 * */ /* * 子View干涉ViewGroup的過程: * 初始化:mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; * 在子View中FLAG_DISALLOW_INTERCEPT被重置,也就是要去干擾, * 然后mGroupFlags & FLAG_DISALLOW_INTERCEPT為1 * 然后(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0 為true * 然后disallowIntercept為true * 然后導致if (!disallowIntercept)無法進入。 * */ /* * FLAG_DISALLOW_INTERCEPT標志位有什么用呢? * 當面對滑動沖突時,我們可以考慮用這種方法去解決問題。 * */ final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { /* * 所以說onInterceptTouchEvent并不是每次事件都會被調用的。 * 而dispatchTouchEvent卻會在每次都調用。 * 對于原始的ViewGroup,onInterceptTouchEvent會返回false, * 但是對于你自己寫的LinearLayout,則可以修改這個函數, * 讓它對ACTION_DOWN、ACTION_MOVE、ACTION_UP做出不同的選擇。 * */ 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. /* * 就是說沒有子元素mFirstTouchTarget,而且事件也不是ACTION_DOWN, * 沒人管那就只能自己攔截了。 * */ intercepted = true; } // Check for cancelation. final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; // Update list of touch targets for pointer down, if needed. final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; /* * 當ViewGroup不攔截事件的時候,intercepted=false,事件會向下分發由它的子View進行處理 * 所以說一旦ViewGroup攔截了事件,intercepted=true, * 意味著事件序列中的任何事件都不再會傳給子元素了,由父元素全權處理。 * 所以intercepted=true一定要謹慎設置。 * */ if (!canceled && !intercepted) { if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { final int actionIndex = ev.getActionIndex(); // always 0 for down final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS; // Clean up earlier touch targets for this pointer id in case they // have become out of sync. removePointersFromTouchTargets(idBitsToAssign); final int childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) { final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); // Find a child that can receive the event. // Scan children from front to back. final View[] children = mChildren; final boolean customOrder = isChildrenDrawingOrderEnabled(); /* * 遍歷ViewGroup的所有子元素,判斷子元素是否能夠接收到點擊事件。 * */ for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = children[childIndex]; /* * 判斷子元素是否能夠接收到點擊事件: * (1)canViewReceivePointerEvents:子元素是否在播動畫。 * (2)isTransformedTouchPointInView:點擊事件的坐標是否落在子元素的區域內。 * */ if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { continue; } /* * 如果上面那個if語句沒有成立,說明這個子元素是可以攔截事件的, * 所以新的TouchTarget出現了,就是這個子元素。 * */ 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); /* * 這個子元素已經攔截該事件了,現在要子元素傳遞給它自己的子元素去分派這個事件了: * dispatchTransformedTouchEvent實際上調用的就是子元素的dispatchTouchEvent方法。 * 下面的第三個參數中child一定不為null,所以child的dispatchTouchEvent一定會被調用。 * 子元素的dispatchTouchEvent返回true, * 意味著dispatchTransformedTouchEvent也返回ture, * 表示事件被子元素分發成功,并break跳出循環。 * */ if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); mLastTouchDownIndex = childIndex; mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); /* * 分發成功后,在addTouchTarget會對mFirstTouchTarget進行賦值 * */ newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; /* * 分發成功,跳出循環 * */ break; } } } if (newTouchTarget == null && mFirstTouchTarget != null) { // Did not find a child to receive the event. // Assign the pointer to the least recently added target. newTouchTarget = mFirstTouchTarget; while (newTouchTarget.next != null) { newTouchTarget = newTouchTarget.next; } newTouchTarget.pointerIdBits |= idBitsToAssign; } } } /* * 有兩種情況遍歷所有的子元素后事件也沒有處理: * (1)ViewGroup根本沒有子元素 * (2)子元素的dispatchTouchEvent都返回了false。 * 這種情況下只能ViewGroup自己來處理事件了。 * */ // Dispatch to touch targets. if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. /* * 注意第三個參數:null,在上面變量子元素的時候這里放的是child。 * 如果是null,dispatchTransformedTouchEvent內部就會調用: * super.dispatchTouchEvent(event); * 很顯然,這里就轉到了View的dispatchTouchEvent(event)方法,即點擊事件開始交由View來處理。在View中有onTouchEvent。 * 其實父元素ViewGroup的onTouchEvent就是指的是View中的onTouchEvent方法,它自己這里是沒有的。因為ViewGroup是繼承View的!!!! * */ handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else { // Dispatch to touch targets, excluding the new touch target if we already // dispatched to it. Cancel touch targets if necessary. TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { final TouchTarget next = target.next; if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; } if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } // Update list of touch targets for pointer up or cancel, if needed. if (canceled || actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { resetTouchState(); } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { final int actionIndex = ev.getActionIndex(); final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); removePointersFromTouchTargets(idBitsToRemove); } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } return handled; } ~~~ View對點擊事件的處理過程 * View源碼中的dispatchTouchEvent進行分析 ~~~ /** * Pass the touch screen motion event down to the target view, or this * view if it is the target. * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ public boolean dispatchTouchEvent(MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement /* * 首先會判斷有沒有設置OnTouchListener。 * 如果OnTouchListener中的onTouch方法返回true,那么onTouchEvent方法就不會調用, * 這樣做的好處是方便外界處理點擊事件。 * */ ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { return true; } /* * 優先級低于OnTouchListener * */ if (onTouchEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } ~~~ * View源碼中的onTouchEvent方法進行分析 ~~~ /** * Implement this method to handle touch screen motion events. * <p> * If this method is used to detect click actions, it is recommended that * the actions be performed by implementing and calling * {@link #performClick()}. This will ensure consistent system behavior, * including: * <ul> * <li>obeying click sound preferences * <li>dispatching OnClickListener calls * <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when * accessibility features are enabled * </ul> * * @param event The motion event. * @return True if the event was handled, false otherwise. */ public boolean onTouchEvent(MotionEvent event) { final int viewFlags = mViewFlags; /* * 當View處于不可用狀態下時,View照樣會消耗點擊事, * 但它并不對事件做出任何的反映 * */ if ((viewFlags & ENABLED_MASK) == DISABLED) { if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } // 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)); } /* * 如果View設置有代理,那么還會執行mTouchDelegate的onTouchEvent方法, * 這個onTouchEvent的工作機制看起來和OnTouchListener類似,這里我們不做研究 * */ if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } } /* * 這里是對點擊事件的具體處理。 * 可以發現的是View的CLICKABLE和LONG_CLICKABLE只要有一個為true, * 那么這個View就消耗這個事件,即onTouchEvent返回ture,不管他是不是DISABLE狀態。 * 這個證明了前面(8)(9)(10)的結論。 * */ if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { /* * 當up事件發生時,就會觸發performClick()方法。 * */ 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 (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); } if (prepressed) { // The button is being released before we actually // showed it as pressed. Make it show the pressed // state now (before scheduling the click) to ensure // the user sees it. setPressed(true); } if (!mHasPerformedLongPress) { // 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)) { /* * 如果View設置了OnClickListener, * 那么performClick()方法內部會調用它的onClick方法 * */ performClick(); } } } if (mUnsetPressedState == null) { mUnsetPressedState = new UnsetPressedState(); } if (prepressed) { postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break; case MotionEvent.ACTION_DOWN: mHasPerformedLongPress = false; if (performButtonActionOnTouchDown(event)) { break; } // Walk up the hierarchy to determine if we're inside a scrolling container. boolean isInScrollingContainer = isInScrollingContainer(); // For views inside a scrolling container, delay the pressed feedback for // a short period in case this is a scroll. if (isInScrollingContainer) { mPrivateFlags |= PFLAG_PREPRESSED; if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { // Not inside a scrolling container, so show the feedback right away setPressed(true); checkForLongClick(0); } break; case MotionEvent.ACTION_CANCEL: setPressed(false); removeTapCallback(); removeLongPressCallback(); break; case MotionEvent.ACTION_MOVE: final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons if (!pointInView(x, y, mTouchSlop)) { // Outside button removeTapCallback(); if ((mPrivateFlags & PFLAG_PRESSED) != 0) { // Remove any future long press/tap checks removeLongPressCallback(); setPressed(false); } } break; } return true; } return 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>

                              哎呀哎呀视频在线观看