Activity中觸摸事件

1.前言

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

為了Window的管理方便暑认,誕生WindowManager,實(shí)現(xiàn)是WindowManagerImpl召衔,WindowManagerImpl直接或間接的存儲(chǔ)DecorView铃诬,ViewRoot,WindowManager;

DecorView,是整個(gè)ViewTree的最頂層View苍凛,它是一個(gè)FrameLayout布局趣席,代表了整個(gè)應(yīng)用的界面,我們setContentView添加的視圖是被mContentParent"包裹"著添加到DecorView 中的。也就是說醇蝴,我們setContentView的View先添加到mContentParent容器宣肚,然后mContentParent又作為DecorView的子View添加到DecorView中。

ViewRoot對(duì)應(yīng)ViewRootImpl類悠栓,它是連接WindowManager和DecorView的紐帶霉涨,View的三大流程均通過ViewRoot來完成。ActivityThread中闸迷,Activity創(chuàng)建#onCreate方法嵌纲,完成DecorView創(chuàng)建動(dòng)作,當(dāng)onCreate()方法執(zhí)行完畢腥沽,后續(xù)ViewRootImpl#setView方法逮走,并把DecorView作為參數(shù)傳遞進(jìn)去,在這個(gè)方法內(nèi)部今阳,會(huì)通過跨進(jìn)程的方式向WMS(WindowManagerService)發(fā)起一個(gè)調(diào)用师溅,從而將DecorView最終添加到Window上茅信。View的繪制流程從ViewRoot的performTraversals方法開始,經(jīng)過measure墓臭、layout和draw三大流程蘸鲸。想要了解以上詳細(xì)內(nèi)容請(qǐng)戳這里

2.Activity窗口接受屏幕觸摸事件的準(zhǔn)備

這里先說明一下Activity對(duì)事件的分發(fā)過程:DecorView -> Activity -> Window -> DecorView

一個(gè)Activity有一個(gè)PhoneWindow窗口,對(duì)應(yīng)一個(gè)頂層ViewParent的實(shí)現(xiàn)類ViewRootImpl,窗口接受屏幕事件的準(zhǔn)備工作是在ViewRootImpl.setView()中進(jìn)行的:

 /**
     * 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.
                // 這里先去向主線程發(fā)個(gè)消息稍后就去發(fā)起視圖樹的測(cè)量,布局,
                // 繪制顯示工作,這樣下面的操作完成后,視圖窗口顯示出來
                // 就可以馬上接受各種輸入事件了
                requestLayout();
                // 一般沒有特別設(shè)置該窗口不能接受輸入事件設(shè)置,這里if==true
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    // 初始化設(shè)置一個(gè)當(dāng)前窗口可以聯(lián)通接受系統(tǒng)輸入事件的的通道
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    // 建立當(dāng)前視圖窗口與系統(tǒng)WindowManagerService服務(wù)的關(guān)聯(lián),
                    //并傳入剛才創(chuàng)建的mInputChannel
                    // 會(huì)在WindowManagerService服務(wù)進(jìn)程為該APP窗口生成兩個(gè)
                    //InputQueue,其中一個(gè)會(huì)調(diào)用InputQueue.transferTo()返回到當(dāng)
                    //前APP進(jìn)程窗口;另外一個(gè)保留在WindowManagerService為當(dāng)
                    //前APP窗口創(chuàng)建的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的內(nèi)部類DecorView對(duì)象,而
                //DecorView extends FrameLayout implements RootViewSurfaceTaker 
                // 所以這里if==ture
                if (view instanceof RootViewSurfaceTaker) {
                    // 創(chuàng)建InputQueue的create和destroy的通知對(duì)象,這里
                    //DecroView.willYouTakeTheInputQueue()一般為null
                    mInputQueueCallback =
                       ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
                }
                // 從上面知道,一般沒有特別設(shè)置該窗口不能接受輸入事件設(shè)置,這
                //里mInputChannel!=null已經(jīng)生成
                if (mInputChannel != null) {
                // 一般情況DecroView.willYouTakeTheInputQueue()為null,所以這
                //里if==false
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    // 創(chuàng)建一個(gè)與當(dāng)前窗口已經(jīng)生成的InputChannel相關(guān)的接受輸入
                    //事件的處理對(duì)象
                    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.
                // 這里設(shè)置當(dāng)前各種不同類別輸入事件到來時(shí)候按對(duì)應(yīng)類型依次分
                //別調(diào)用的處理對(duì)象
                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()中,會(huì)建立當(dāng)前視圖窗口體系與系WindowManagerService服務(wù)的關(guān)聯(lián),并在系統(tǒng)WindowManagerService服務(wù)為該窗口生成兩個(gè)InputChannel輸入事件通道,一個(gè)轉(zhuǎn)移到當(dāng)前頂層ViewParent即ViewRootImpl中,并在ViewRootImpl生成一個(gè)與輸入事件通道關(guān)聯(lián)的事件處理WindowInputEventReceiver內(nèi)部類對(duì)象mInputEventReceiver:

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

    // 重寫了父類onInputEvent,調(diào)用enqueueInputEvent實(shí)際處理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")
    // 當(dāng)輸入事件到來時(shí)該方法由native層代碼發(fā)起調(diào)用
    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);
    }
    ....

}

至此可以看到,一個(gè)屏幕輸入事件返回處理在當(dāng)前視圖窗口WindowInputEventReceiver內(nèi)部類的onInputEvent(InputEvent event)中,隨即調(diào)用了外部類ViewRootImpl.enqueueInputEvent(event, this, 0, true),需要注意的是這里的參數(shù)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();
    }
}

先是獲取一個(gè)指向當(dāng)前事件的輸入事件隊(duì)列QueuedInputEvent對(duì)象,然后根據(jù)情況賦值ViewRootImpl成員變量mPendingInputEventHead或者追加到mPendingInputEventTail的mNext尾部,隨即調(diào)用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即當(dāng)前待處理事件隊(duì)列還有事件需要去被處理掉,就一直循環(huán)調(diào)用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);
    }
}

這里分別有兩個(gè)判斷,q.shouldSendToSynthesizer()和q.shouldSkipIme(),上面WindowInputEventReceiver拿到native返回的一個(gè)輸入事件對(duì)象時(shí)候,調(diào)用的ViewRootImpl.enqueueInputEvent(event, this, 0, true),標(biāo)記了輸入事件的QueuedInputEvent對(duì)象至此為止falg==0,所以,這里一般情況調(diào)用在setView()中生成的NativePreImeInputStage mFirstInputStage對(duì)象接著去處理.從setView()方法中可知,一共生成了7個(gè)InputStage的子類對(duì)象依次接龍按事件類型對(duì)應(yīng)去處理,入口是NativePreImeInputStage該子類對(duì)象,NativePreImeInputStage的頂層父類當(dāng)然也是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);
        }
    }    
    ....
}

從構(gòu)造就可以看出,每個(gè)生成的InputStage對(duì)象都會(huì)有一個(gè)成員變量mNext指向下一個(gè)處理事件的InputStage對(duì)象;
deliver()方法先判斷該事件對(duì)象是否已經(jīng)處理完成或者需要拋棄掉,都不滿足則調(diào)用onProcess()處理該事件對(duì)象,處理完成后返回處理結(jié)果給apply()方法后續(xù)工作,根據(jù)onProcess()返回處理結(jié)果是否把事件傳遞給其mNext指向的下一個(gè)InputStage去處理;
當(dāng)然具體處理是在子類的onProcess()中實(shí)現(xiàn)的了

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);
    }
}

對(duì)于屏幕觸摸事件,這里NativePreImeInputStage的onProcess()返回FORWARD,即交給其mNext即ViewPreImeInputStage去接龍?zhí)幚?/p>

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

對(duì)于觸摸事件ViewPreImeInputStage.onProcess()同樣返回FORWARD,交給其其mNext即ViewPreImeInputStage去接龍?zhí)幚鞩meInputStage去處理

 /**
 * 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);
    }
}

很明顯這里是處理輸入法事件的,對(duì)于一般觸摸事件同樣返回FORWARD交給其mNext 即EarlyPostImeInputStage去處理,查看EarlyPostImeInputStage源碼可知其對(duì)于觸摸事件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;
    }

    ....

}

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

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

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

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

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

所以這里,調(diào)用了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去分發(fā)事件了

3. Activity中的dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {
    // onUserInteraction默認(rèn)不執(zhí)行任何動(dòng)作。
    // 它是提供給客戶的接口窿锉。
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    // 這里會(huì)調(diào)用到ViewGroup的dispatchTouchEvent()酌摇,
    // 即會(huì)調(diào)用Activity包含的根視圖的dispatchTouchEvent()。
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    // 如果superDispatchTouchEvent()返回false嗡载,
    // 即Activity的根視圖以及根視圖的子視圖都沒有攔截該事件的話窑多,則調(diào)用Activity的onTouchEvent()
    return onTouchEvent(ev);
}

這里重點(diǎn)需要了解:Activity在通過dispatchTouchEvent()傳遞觸摸事件的時(shí)候,會(huì)調(diào)用到ViewGroup的dispatchTouchEvent()洼滚。從而實(shí)現(xiàn)埂息,將Activity中的觸摸事件傳遞給它所包含的View或ViewGroup。

如果Activity中的視圖都沒有對(duì)觸摸事件進(jìn)行攔截的話遥巴,則調(diào)用Activity的onTouchEvent()對(duì)觸摸事件進(jìn)行處理千康。

4. Activity中的onTouchEvent

從上面我們了解到

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

如果superDispatchTouchEvent()返回false的話,意味著铲掐,Activity所包含的視圖都沒有攔截或消費(fèi)該觸摸事件拾弃;那么,就會(huì)調(diào)用Activity的onTouchEvent()來處理觸摸事件迹炼。
下面就看看onTouchEvent()的代碼砸彬。

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

    return false;
}   

代碼很簡(jiǎn)單。它會(huì)先調(diào)用mWindow.shouldCloseOnTouch()斯入,如果shouldCloseOnTouch()返回true砂碉,則意味著該觸摸事件會(huì)觸發(fā)"結(jié)束Activity"的動(dòng)作。那么接下來刻两,就調(diào)用finish()來結(jié)束Activity增蹭,并返回true,表示Activity消費(fèi)了這個(gè)觸摸事件磅摹。否則的話滋迈,就返回false。

我們知道m(xù)Window是PhoneWindow對(duì)象户誓,而PhoneWindow繼承于Window饼灿。則mWindow.shouldCloseOnTouch()實(shí)際上會(huì)調(diào)用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是一個(gè)boolean變量帝美,它是由Window的android:windowCloseOnTouchOutside屬性值決定碍彭。

isOutOfBounds(context, event)是判斷該event的坐標(biāo)是否在context(對(duì)于本文來說就是當(dāng)前的Activity)之外。是的話,返回true庇忌;否則舞箍,返回false。

peekDecorView()則是返回PhoneWindow的mDecor皆疹。

也就是說疏橄,如果設(shè)置了android:windowCloseOnTouchOutside屬性為true,并且當(dāng)前事件是ACTION_DOWN略就,而且點(diǎn)擊發(fā)生在Activity之外捎迫,同時(shí)Activity還包含視圖的話,則返回true残制;表示該點(diǎn)擊事件會(huì)導(dǎo)致Activity的結(jié)束立砸。

總結(jié)

ViewRootImpl對(duì)輸入事件的準(zhǔn)備工作以及經(jīng)過一系列處理把觸摸事件交由頂層視圖DecorView掖疮。DecorView通過重寫dispatchPointerEvent方法初茶,將事件傳遞給Activity。Activity通過dispatchTouchEvent調(diào)用到Activity所屬Window的superDispatchTouchEvent浊闪,進(jìn)而調(diào)用到Window的DecorView的superDispatchTouchEvent恼布,進(jìn)一步的又調(diào)用到ViewGroup的dispatchTouchEvent()。

如果Activity所包含的視圖攔截或者消費(fèi)了該觸摸事件的話搁宾,就不會(huì)再執(zhí)行Activity的onTouchEvent()折汞;

如果Activity所包含的視圖沒有攔截或者消費(fèi)該觸摸事件的話,則會(huì)執(zhí)行Activity的onTouchEvent()盖腿。

Activity中的onTouchEvent是Activity自身對(duì)觸摸事件的處理爽待。如果該Activity的android:windowCloseOnTouchOutside屬性為true,并且當(dāng)前觸摸事件是ACTION_DOWN翩腐,而且該觸摸事件的坐標(biāo)在Activity之外鸟款,同時(shí)Activity還包含了視圖的話;就會(huì)導(dǎo)致Activity被結(jié)束茂卦。

activity事件傳遞.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末何什,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子等龙,更是在濱河造成了極大的恐慌处渣,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛛砰,死亡現(xiàn)場(chǎng)離奇詭異罐栈,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)泥畅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門荠诬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事浅妆⊥” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵凌外,是天一觀的道長(zhǎng)辩尊。 經(jīng)常有香客問我,道長(zhǎng)康辑,這世上最難降的妖魔是什么摄欲? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮疮薇,結(jié)果婚禮上胸墙,老公的妹妹穿的比我還像新娘。我一直安慰自己按咒,他們只是感情好迟隅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著励七,像睡著了一般智袭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掠抬,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天吼野,我揣著相機(jī)與錄音,去河邊找鬼两波。 笑死瞳步,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的腰奋。 我是一名探鬼主播单起,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼氛堕!你這毒婦竟也來了馏臭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤讼稚,失蹤者是張志新(化名)和其女友劉穎括儒,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锐想,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡帮寻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年屋谭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迄本。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拘领,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出烫罩,到底是詐尸還是另有隱情惜傲,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布贝攒,位于F島的核電站盗誊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏隘弊。R本人自食惡果不足惜哈踱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梨熙。 院中可真熱鬧开镣,春花似錦、人聲如沸咽扇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肌割。三九已至卧蜓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間把敞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工榨惠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奋早,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓赠橙,卻偏偏與公主長(zhǎng)得像耽装,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子期揪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容