Android--点击事件的分发(1)

在事件分发和处理的过程中,我们比较熟悉方法有:比如dispatchTouchEvent,做事件分发;onTouchEvent,onTouchListener用来处理各类型的事件等,但是事件到底是从哪里开始的呢?又是怎么传递到dispatchTouchEvent方法,这些我们都没有关心过,今天就带着大家简单过一下源码

相信大家应该还没忘记ViewRootImpl这个类,在上篇博客Android-UI的渲染过程中提到过它的setView方法,主要解释了里面requestLayout方法,今天我们看一下其他的内容:

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                .....

                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

                ......

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

第17~26行:我们可以看见一个关键的类InputStage以及它的子类类,这些类表示的就是点击的类型。这里的类型有很多中,关于这些类型的介绍,我推荐大家一篇文章Android-InputStage,而我们这次所说的点击事件类型对应的InputStage是ViewPostImeInputStage

第11行:创建了一个WindowInputEventReceiver对象,当发生点击事件的时候,这个就会执行到这个类的onInputEvent方法,我们看一下这个方法

    void enqueueInputEvent(InputEvent event) {
        enqueueInputEvent(event, null, 0, false);
    }

查看enqueueInputEvent方法

    void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        ....

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

这里就是做了一个线程调度,但是最终都会执行到doProcessInputEvents方法中

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

我留下了关键方法deliverInputEvent,我们进入看一下

    private void deliverInputEvent(QueuedInputEvent q) {
        Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
                q.mEvent.getSequenceNumber());
        ...

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

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

第6行:InputStage对象和我们上面说的InputStage对应,这里是ViewPostImeInputStage

第16行:deliver在ViewPostImeInputStage中没有被复写,所以我们查看的InputStage的deliver方法

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

这里会执行到else的方法体重,我们在apply方法中看见onProcess方法,而这个方法在ViewPostImeInputStage中被复写,我们查看一下:

        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                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);
                }
            }
        }

会执行到第8行的processPointerEvent方法,我们查看:

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

            ....
            boolean handled = mView.dispatchPointerEvent(event);
            ....
            return handled ? FINISH_HANDLED : FORWARD;
        }

到这里,我们要注意,我们目前看的这些类都是ViewRootImpl的内部类,而这里的mView就是ViewRootImpl中的mView。相信大家应该还没忘记,ViewRootImpl的mView是DecorView,我们查看DecorView的dispatchPointerEvent方法,我们在DecorView中并没有找到这个方法,那么这个类一定是在它的父类,或者父类的父类里,其实是在View中,我们查看:

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

dispatchTouchEvent方法在DecorView中有复写,我们查看DecorView的此方法

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final Window.Callback cb = mWindow.getCallback();
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }

cb这个对象其实就是Activity,这里执行了Activity的dispatchTouchEvent方法,继续查看

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

这里存在了两种情况,我们先看第一种,也就是getWindow().superDispatchTouchEvent方法返回true的情况,getWindow()估计大家都不陌生了,就是PhoneWindow对象,我们查看它的superDispatchTouchEvent方法

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

mDecor是DecorView,让了一圈有回到了这个类,我们继续查看它的superDispatchTouchEvent方法

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

DecorView的父类是FrameLayout,这个类是没有复写dispatchTouchEvent方法,也就是没有做事件的处理,实际上是ViewGroup处理的。

其实到这里,事件的分发的第一个阶段就结束了,基本上捋清了事件是如何从Activity传递到DecorView的过程。

我们刚刚在Activity的dispatchTouchEvent方法中说,有两种情况,也就是说如果getWindow().superDispatchTouchEvent方法为false时候,是第二种情况,表示每一个View或ViewGroup都不对事件进行分发拦截,那么由最上层的View进行事件的处理,也就是执行Activity的onTouchEvent方法。

关于事件分发的第二阶段,也就是事件是如何在View树中传递的,我会在下一篇博客介绍。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值