Activity中觸摸事件

1.前言

Activity并不會直接與UI交互的,而是通過Window("UI界面的外框")的子類,如PhoneWindow來交互的。

為了Window的管理方便,誕生WindowManager,實現是WindowManagerImpl,WindowManagerImpl直接或間接的存儲DecorView,ViewRoot,WindowManager;

DecorView,是整個ViewTree的最頂層View,它是一個FrameLayout布局,代表了整個應用的界面,我們setContentView添加的視圖是被mContentParent"包裹"著添加到DecorView 中的。也就是說,我們setContentView的View先添加到mContentParent容器,然后mContentParent又作為DecorView的子View添加到DecorView中。

ViewRoot對應ViewRootImpl類,它是連接WindowManager和DecorView的紐帶,View的三大流程均通過ViewRoot來完成。ActivityThread中,Activity創建#onCreate方法,完成DecorView創建動作,當onCreate()方法執行完畢,后續ViewRootImpl#setView方法,并把DecorView作為參數傳遞進去,在這個方法內部,會通過跨進程的方式向WMS(WindowManagerService)發起一個調用,從而將DecorView最終添加到Window上。View的繪制流程從ViewRoot的performTraversals方法開始,經過measure、layout和draw三大流程。想要了解以上詳細內容請戳這里

2.Activity窗口接受屏幕觸摸事件的準備

這里先說明一下Activity對事件的分發過程:DecorView -> Activity -> Window -> DecorView

一個Activity有一個PhoneWindow窗口,對應一個頂層ViewParent的實現類ViewRootImpl,窗口接受屏幕事件的準備工作是在ViewRootImpl.setView()中進行的:

 /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
               ....

                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                // 這里先去向主線程發個消息稍后就去發起視圖樹的測量,布局,
                // 繪制顯示工作,這樣下面的操作完成后,視圖窗口顯示出來
                // 就可以馬上接受各種輸入事件了
                requestLayout();
                // 一般沒有特別設置該窗口不能接受輸入事件設置,這里if==true
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    // 初始化設置一個當前窗口可以聯通接受系統輸入事件的的通道
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    // 建立當前視圖窗口與系統WindowManagerService服務的關聯,
                    //并傳入剛才創建的mInputChannel
                    // 會在WindowManagerService服務進程為該APP窗口生成兩個
                    //InputQueue,其中一個會調用InputQueue.transferTo()返回到當
                    //前APP進程窗口;另外一個保留在WindowManagerService為當
                    //前APP窗口創建的WindowState中
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
                } catch (RemoteException e) {
                    ...
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }

             ...
                // 很明顯,view是PhoneWindow的內部類DecorView對象,而
                //DecorView extends FrameLayout implements RootViewSurfaceTaker 
                // 所以這里if==ture
                if (view instanceof RootViewSurfaceTaker) {
                    // 創建InputQueue的create和destroy的通知對象,這里
                    //DecroView.willYouTakeTheInputQueue()一般為null
                    mInputQueueCallback =
                       ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
                }
                // 從上面知道,一般沒有特別設置該窗口不能接受輸入事件設置,這
                //里mInputChannel!=null已經生成
                if (mInputChannel != null) {
                // 一般情況DecroView.willYouTakeTheInputQueue()為null,所以這
                //里if==false
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    // 創建一個與當前窗口已經生成的InputChannel相關的接受輸入
                    //事件的處理對象
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

                view.assignParent(this);
                mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
                mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;

                if (mAccessibilityManager.isEnabled()) {
                    mAccessibilityInteractionConnectionManager.ensureConnection();
                }

                if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                    view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
                }

                // Set up the input pipeline.
                // 這里設置當前各種不同類別輸入事件到來時候按對應類型依次分
                //別調用的處理對象
                CharSequence counterSuffix = attrs.getTitle();
                mSyntheticInputStage = new SyntheticInputStage();
                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
                InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
                InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
                InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                        "aq:native-pre-ime:" + counterSuffix);

                mFirstInputStage = nativePreImeStage;
                mFirstPostImeInputStage = earlyPostImeStage;
                mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
            }
        }
    }

從上面看出,在窗口初始化即ViewRootImpl.setView()中,會建立當前視圖窗口體系與系WindowManagerService服務的關聯,并在系統WindowManagerService服務為該窗口生成兩個InputChannel輸入事件通道,一個轉移到當前頂層ViewParent即ViewRootImpl中,并在ViewRootImpl生成一個與輸入事件通道關聯的事件處理WindowInputEventReceiver內部類對象mInputEventReceiver:

final class WindowInputEventReceiver extends InputEventReceiver {
    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
        super(inputChannel, looper);
    }

    // 重寫了父類onInputEvent,調用enqueueInputEvent實際處理native層返回的InputEvent輸入事件
    @Override
    public void onInputEvent(InputEvent event) {
        enqueueInputEvent(event, this, 0, true);
    }

    @Override
    public void onBatchedInputEventPending() {
        if (mUnbufferedInputDispatch) {
            super.onBatchedInputEventPending();
        } else {
            scheduleConsumeBatchedInput();
        }
    }

    @Override
    public void dispose() {
        unscheduleConsumeBatchedInput();
        super.dispose();
    }
}

// 看下WindowInputEventReceiver父類
public abstract class InputEventReceiver {

    ....

    // Called from native code.
    @SuppressWarnings("unused")
    // 當輸入事件到來時該方法由native層代碼發起調用
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }
    /**
     * Called when an input event is received.
     * The recipient should process the input event and then call {@link #finishInputEvent}
     * to indicate whether the event was handled.  No new input events will be received
     * until {@link #finishInputEvent} is called.
     *
     * @param event The input event that was received.
     */
    public void onInputEvent(InputEvent event) {
        finishInputEvent(event, false);
    }
    ....

}

至此可以看到,一個屏幕輸入事件返回處理在當前視圖窗口WindowInputEventReceiver內部類的onInputEvent(InputEvent event)中,隨即調用了外部類ViewRootImpl.enqueueInputEvent(event, this, 0, true),需要注意的是這里的參數flags==0

void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    adjustInputEventForCompatibility(event);
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

    // Always enqueue the input event in order, regardless of its time stamp.
    // We do this because the application or the IME may inject key events
    // in response to touch events and we want to ensure that the injected keys
    // are processed in the order they were received and we cannot trust that
    // the time stamp of injected events are monotonic.
    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    mPendingInputEventCount += 1;
    Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
            mPendingInputEventCount);

    if (processImmediately) {
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}

先是獲取一個指向當前事件的輸入事件隊列QueuedInputEvent對象,然后根據情況賦值ViewRootImpl成員變量mPendingInputEventHead或者追加到mPendingInputEventTail的mNext尾部,隨即調用doProcessInputEvents()

void doProcessInputEvents() {
    // Deliver all pending input events in the queue.
    while (mPendingInputEventHead != null) {
        QueuedInputEvent q = mPendingInputEventHead;
        mPendingInputEventHead = q.mNext;
        if (mPendingInputEventHead == null) {
            mPendingInputEventTail = null;
        }
        q.mNext = null;

        mPendingInputEventCount -= 1;
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                mPendingInputEventCount);

        long eventTime = q.mEvent.getEventTimeNano();
        long oldestEventTime = eventTime;
        if (q.mEvent instanceof MotionEvent) {
            MotionEvent me = (MotionEvent)q.mEvent;
            if (me.getHistorySize() > 0) {
                oldestEventTime = me.getHistoricalEventTimeNano(0);
            }
        }
        mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);

        deliverInputEvent(q);
    }

    // We are done processing all input events that we can process right now
    // so we can clear the pending flag immediately.
    if (mProcessInputEventsScheduled) {
        mProcessInputEventsScheduled = false;
        mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
    }
}

只要mPendingInputEventHead!=null即當前待處理事件隊列還有事件需要去被處理掉,就一直循環調用deliverInputEvent()

private void deliverInputEvent(QueuedInputEvent q) {
    Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
            q.mEvent.getSequenceNumber());
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
    }

    InputStage stage;
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }

    if (stage != null) {
        stage.deliver(q);
    } else {
        finishInputEvent(q);
    }
}

這里分別有兩個判斷,q.shouldSendToSynthesizer()和q.shouldSkipIme(),上面WindowInputEventReceiver拿到native返回的一個輸入事件對象時候,調用的ViewRootImpl.enqueueInputEvent(event, this, 0, true),標記了輸入事件的QueuedInputEvent對象至此為止falg==0,所以,這里一般情況調用在setView()中生成的NativePreImeInputStage mFirstInputStage對象接著去處理.從setView()方法中可知,一共生成了7個InputStage的子類對象依次接龍按事件類型對應去處理,入口是NativePreImeInputStage該子類對象,NativePreImeInputStage的頂層父類當然也是InputStage:

abstract class InputStage {
    private final InputStage mNext;

    protected static final int FORWARD = 0;
    protected static final int FINISH_HANDLED = 1;
    protected static final int FINISH_NOT_HANDLED = 2;

    /**
     * Creates an input stage.
     * @param next The next stage to which events should be forwarded.
     */
    public InputStage(InputStage next) {
        mNext = next;
    }

    /**
     * Delivers an event to be processed.
     */
    public final void deliver(QueuedInputEvent q) {
        if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
            forward(q);
        } else if (shouldDropInputEvent(q)) {
            finish(q, false);
        } else {
            apply(q, onProcess(q));
        }
    }

    /**
     * Marks the the input event as finished then forwards it to the next stage.
     */
    protected void finish(QueuedInputEvent q, boolean handled) {
        q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
        if (handled) {
            q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
        }
        forward(q);
    }

    /**
     * Forwards the event to the next stage.
     */
    protected void forward(QueuedInputEvent q) {
        onDeliverToNext(q);
    }

    /**
     * Applies a result code from {@link #onProcess} to the specified event.
     */
    protected void apply(QueuedInputEvent q, int result) {
        if (result == FORWARD) {
            forward(q);
        } else if (result == FINISH_HANDLED) {
            finish(q, true);
        } else if (result == FINISH_NOT_HANDLED) {
            finish(q, false);
        } else {
            throw new IllegalArgumentException("Invalid result: " + result);
        }
    }

    /**
     * Called when an event is ready to be processed.
     * @return A result code indicating how the event was handled.
     */
    protected int onProcess(QueuedInputEvent q) {
        return FORWARD;
    }

    /**
     * Called when an event is being delivered to the next stage.
     */
    protected void onDeliverToNext(QueuedInputEvent q) {
        if (DEBUG_INPUT_STAGES) {
            Log.v(TAG, "Done with " + getClass().getSimpleName() + ". " + q);
        }
        if (mNext != null) {
            mNext.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }    
    ....
}

從構造就可以看出,每個生成的InputStage對象都會有一個成員變量mNext指向下一個處理事件的InputStage對象;
deliver()方法先判斷該事件對象是否已經處理完成或者需要拋棄掉,都不滿足則調用onProcess()處理該事件對象,處理完成后返回處理結果給apply()方法后續工作,根據onProcess()返回處理結果是否把事件傳遞給其mNext指向的下一個InputStage去處理;
當然具體處理是在子類的onProcess()中實現的了

final class NativePreImeInputStage extends AsyncInputStage
        implements InputQueue.FinishedInputEventCallback {
    public NativePreImeInputStage(InputStage next, String traceCounter) {
        super(next, traceCounter);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
            mInputQueue.sendInputEvent(q.mEvent, q, true, this);
            return DEFER;
        }
        return FORWARD;
    }

    @Override
    public void onFinishedInputEvent(Object token, boolean handled) {
        QueuedInputEvent q = (QueuedInputEvent)token;
        if (handled) {
            finish(q, true);
            return;
        }
        forward(q);
    }
}

對于屏幕觸摸事件,這里NativePreImeInputStage的onProcess()返回FORWARD,即交給其mNext即ViewPreImeInputStage去接龍處理

final class ViewPreImeInputStage extends InputStage {
   public ViewPreImeInputStage(InputStage next) {
       super(next);
   }

   @Override
   protected int onProcess(QueuedInputEvent q) {
       if (q.mEvent instanceof KeyEvent) {
           return processKeyEvent(q);
       }
       return FORWARD;
   }

   private int processKeyEvent(QueuedInputEvent q) {
       final KeyEvent event = (KeyEvent)q.mEvent;
       if (mView.dispatchKeyEventPreIme(event)) {
           return FINISH_HANDLED;
       }
       return FORWARD;
   }

對于觸摸事件ViewPreImeInputStage.onProcess()同樣返回FORWARD,交給其其mNext即ViewPreImeInputStage去接龍處理ImeInputStage去處理

 /**
 * Delivers input events to the ime.
 * Does not support pointer events.
 */
final class ImeInputStage extends AsyncInputStage
        implements InputMethodManager.FinishedInputEventCallback {
    public ImeInputStage(InputStage next, String traceCounter) {
        super(next, traceCounter);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (mLastWasImTarget && !isInLocalFocusMode()) {
            InputMethodManager imm = InputMethodManager.peekInstance();
            if (imm != null) {
                final InputEvent event = q.mEvent;
                if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
                int result = imm.dispatchInputEvent(event, q, this, mHandler);
                if (result == InputMethodManager.DISPATCH_HANDLED) {
                    return FINISH_HANDLED;
                } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
                    // The IME could not handle it, so skip along to the next InputStage
                    return FORWARD;
                } else {
                    return DEFER; // callback will be invoked later
                }
            }
        }
        return FORWARD;
    }

    @Override
    public void onFinishedInputEvent(Object token, boolean handled) {
        QueuedInputEvent q = (QueuedInputEvent)token;
        if (handled) {
            finish(q, true);
            return;
        }
        forward(q);
    }
}

很明顯這里是處理輸入法事件的,對于一般觸摸事件同樣返回FORWARD交給其mNext 即EarlyPostImeInputStage去處理,查看EarlyPostImeInputStage源碼可知其對于觸摸事件onProcess()返回FORWARD交給其mNext 即NativePostImeInputStage去處理,而NativePostImeInputStage.onProcess()同樣返回FORWARD交給其mNext 即ViewPostImeInputStage去處理,看出ViewPostImeInputStage.onProcess():

final class ViewPostImeInputStage extends InputStage {

    ....

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (q.mEvent instanceof KeyEvent) {
            return processKeyEvent(q);
        } else {
            // If delivering a new non-key event, make sure the window is
            // now allowed to start updating.
            handleDispatchWindowAnimationStopped();
            final int source = q.mEvent.getSource();
            // Whoops here!!
            if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                return processPointerEvent(q);
            } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                return processTrackballEvent(q);
            } else {
                return processGenericMotionEvent(q);
            }
        }
    }

    private int processPointerEvent(QueuedInputEvent q) {
        final MotionEvent event = (MotionEvent)q.mEvent;

        mAttachInfo.mUnbufferedDispatchRequested = false;
        boolean handled = mView.dispatchPointerEvent(event);
        if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
            mUnbufferedInputDispatch = true;
            if (mConsumeBatchedInputScheduled) {
                scheduleConsumeBatchedInputImmediately();
            }
        }
        return handled ? FINISH_HANDLED : FORWARD;
    }

    ....

}

對于觸摸事件,這里的if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0)為ture,所以調用processPointerEvent()去處理,在processPointerEvent()中直接調用mView.dispatchPointerEvent(event),而mView即窗口的頂層視圖DecroView;

至此,終于看到ViewRootImpl對輸入事件的準備工作以及經過一系列處理把觸摸事件交由頂層視圖DecroView的過程,DecroView和其父類FrameLayout,ViewGroup均沒有重寫此方法,故在View.dispatchPointerEvent()中:

public final boolean dispatchPointerEvent(MotionEvent event) {
    if (event.isTouchEvent()) {
        return dispatchTouchEvent(event);
    } else {
        return dispatchGenericMotionEvent(event);
    }
}

很明顯,這里調用了dispatchTouchEvent()去處理,而DecorView重新了該方法:

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // 在Activity.attach()中已經把自己設置賦值到DecroView的外部類Window的Callback mCallback成員變量
        // 且在PhoneWindow生成DecroView對象的時候傳入的mFeatureId=-1
        final Callback cb = getCallback();
        return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
                : super.dispatchTouchEvent(ev);
    }

所以這里,調用了Acitivity.dispatchTouchEvent()去處理

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

又讓PhoneWindow.superDispatchTouchEvent()處理:

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

又讓DecroView.superDispatchTouchEvent()處理:

    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

DecroView繼承之FrameLayout,FrameLayout繼承之ViewGroup,所以事件終于到了ViewGroup的dispatchTouchEvent()中去處理了!

到這里,事件終于到了ViewGroup.dispatchTouchEvent()了.

剩下的工作就是 GroupView的dispatchTouchEvent去分發事件了

3. Activity中的dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {
    // onUserInteraction默認不執行任何動作。
    // 它是提供給客戶的接口。
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    // 這里會調用到ViewGroup的dispatchTouchEvent(),
    // 即會調用Activity包含的根視圖的dispatchTouchEvent()。
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    // 如果superDispatchTouchEvent()返回false,
    // 即Activity的根視圖以及根視圖的子視圖都沒有攔截該事件的話,則調用Activity的onTouchEvent()
    return onTouchEvent(ev);
}

這里重點需要了解:Activity在通過dispatchTouchEvent()傳遞觸摸事件的時候,會調用到ViewGroup的dispatchTouchEvent()。從而實現,將Activity中的觸摸事件傳遞給它所包含的View或ViewGroup。

如果Activity中的視圖都沒有對觸摸事件進行攔截的話,則調用Activity的onTouchEvent()對觸摸事件進行處理。

4. Activity中的onTouchEvent

從上面我們了解到

 if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
 return onTouchEvent(ev);

如果superDispatchTouchEvent()返回false的話,意味著,Activity所包含的視圖都沒有攔截或消費該觸摸事件;那么,就會調用Activity的onTouchEvent()來處理觸摸事件。
下面就看看onTouchEvent()的代碼。

public boolean onTouchEvent(MotionEvent event) {
    if (mWindow.shouldCloseOnTouch(this, event)) {
        finish();
        return true;
    }    

    return false;
}   

代碼很簡單。它會先調用mWindow.shouldCloseOnTouch(),如果shouldCloseOnTouch()返回true,則意味著該觸摸事件會觸發"結束Activity"的動作。那么接下來,就調用finish()來結束Activity,并返回true,表示Activity消費了這個觸摸事件。否則的話,就返回false。

我們知道mWindow是PhoneWindow對象,而PhoneWindow繼承于Window。則mWindow.shouldCloseOnTouch()實際上會調用Window中的shouldCloseOnTouch()。

public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
    if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
            && isOutOfBounds(context, event) && peekDecorView() != null) {
        return true;
    }    
    return false;
}    

說明mCloseOnTouchOutside是一個boolean變量,它是由Window的android:windowCloseOnTouchOutside屬性值決定。

isOutOfBounds(context, event)是判斷該event的坐標是否在context(對于本文來說就是當前的Activity)之外。是的話,返回true;否則,返回false。

peekDecorView()則是返回PhoneWindow的mDecor。

也就是說,如果設置了android:windowCloseOnTouchOutside屬性為true,并且當前事件是ACTION_DOWN,而且點擊發生在Activity之外,同時Activity還包含視圖的話,則返回true;表示該點擊事件會導致Activity的結束。

總結

ViewRootImpl對輸入事件的準備工作以及經過一系列處理把觸摸事件交由頂層視圖DecorView。DecorView通過重寫dispatchPointerEvent方法,將事件傳遞給Activity。Activity通過dispatchTouchEvent調用到Activity所屬Window的superDispatchTouchEvent,進而調用到Window的DecorView的superDispatchTouchEvent,進一步的又調用到ViewGroup的dispatchTouchEvent()。

如果Activity所包含的視圖攔截或者消費了該觸摸事件的話,就不會再執行Activity的onTouchEvent();

如果Activity所包含的視圖沒有攔截或者消費該觸摸事件的話,則會執行Activity的onTouchEvent()。

Activity中的onTouchEvent是Activity自身對觸摸事件的處理。如果該Activity的android:windowCloseOnTouchOutside屬性為true,并且當前觸摸事件是ACTION_DOWN,而且該觸摸事件的坐標在Activity之外,同時Activity還包含了視圖的話;就會導致Activity被結束。

activity事件傳遞.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容