美文网首页
Activity中触摸事件

Activity中触摸事件

作者: colin_default | 来源:发表于2017-07-24 18:32 被阅读0次

    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

    相关文章

      网友评论

          本文标题:Activity中触摸事件

          本文链接:https://www.haomeiwen.com/subject/eeobkxtx.html