《Android》事件传递过程

1、什么是事件传递?
Android事件传递是指用户操作屏幕产生的一系列动作事件(按下、滑动、抬起)从外层传递到的内层的过程。

2、外层到内层如何理解?
Activity —> Window ----> ViewGroup ---->View

3、必须了解的基础
3.1 首先要知道传递的对象是一个MotionEvent类的对象。这个类中定义了动作常量,比如“按下” public static final int ACTION_DOWN = 0等等。
3.2 第二要知道3个重要的方法 :
boolean dispatchTouchEvent(MotionEvent ev) 分发事件ev
boolean onInterceptTouchEvent(MotionEvent ev) 是否拦截事件ev
boolean onTouchEvent(MotionEvent ev) 是否消耗事件ev
3.3经常给view设置的有关事件监听的方法
view.setOnTouchListener(…) 触摸事件监听
view.setOnClickListener(…) 单击事件监听

4、事件传递的具体过程
4.1产生事件:
手指触碰屏幕首先产生一个MotionEvent Down事件,随着手指滑动产生多个MotionEvent Move事件,最后抬起手指产生一个MotionEvent Up事件
4.2 事件在Activity中:
Activity中有dispatchTouchEvent()、onTouchEvent()2个方法。

 /**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     *
     * @param ev The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
     /**
     * Called when a touch screen event was not handled by any of the views
     * under it.  This is most useful to process touch events that happen
     * outside of your window bounds, where there is no view to receive it.
     *
     * @param event The touch screen event being processed.
     *
     * @return Return true if you have consumed the event, false if you haven't.
     * The default implementation always returns false.
     */
    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }

dispatchTouchEvent中事件先去传给getWindow().superDispatchTouchEvent(ev)去做判断,如果返回为true,则直接结束。如果为false 触发Activity的onTouchEvent()方法。
也就是说在Activity中通过getWindow().superDispatchTouchEvent(ev)方法将事件传递给了Window。
4.3 事件在Window中:
getWindow().superDispatchTouchEvent(ev)调用Window内部的superDispatchTouchEvent()方法。

 /**
     * Used by custom windows, such as Dialog, to pass the touch screen event
     * further down the view hierarchy. Application developers should
     * not need to implement or call this.
     *
     */
    public abstract boolean superDispatchTouchEvent(MotionEvent event);

这是一个抽象方法。具体逻辑在Window的唯一继承类PhoneWidow中:

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

事件在window中没有做任务处理,直接通过mDecor.superDispatchTouchEvent(event)传递给了DecorView。DecorView是view的最顶层,被定义在PhoneWindow中的一个对象。它是一个继承自FrameLayout的ViewGroup。

// This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;

4.4 事件在ViewGroup中:
事件传递到DecorView的superDispatchTouchEvent()方法

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

调用了super.dispatchTouchEvent(event),这个super其实就是ViewGroup。
在ViewGroup中有dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()3个方法。
由于源码过长,其中还包含很多控制单元,为了分析过程直接贴出来不直观,不想篇幅过长,自己去查看印象更深刻!直接总结:
(1)事件MotionEvent传递给dispatchTouchEvent(ev)进行事件分发
(2)在分发方法内部调用了onInterceptTouchEvent(ev)判读是否对事件拦截(返回false代表不拦截,true表示拦截。默认为false)。
(3) onInterceptTouchEvent(ev)返回true, 说明要拦截事件,ev会交给此viewGroup处理。
(4)如果viewGroup设置了OnTouchListener,onTouch()会被调用,onTouch()返回false viewGroup的onTouchEvent会被调用。onTouch()返回true viewGroup的onTouchEvent会被屏蔽掉。
(5)onInterceptTouchEvent(ev)返回false,不拦截事件,将会把事件传递给 子View(view或viewGroup)的dispatchTouchEvent(ev)处理。如果子view是ViewGroup则继续从(1)开始循环。

4.4 事件在View中:
在View中有dispatchTouchEvent()、onTouchEvent()2个方法。没有onInterceptTouchEvent()
与ViewGroup过程相似,毕竟ViewGroup继承自View。ViewGroup也没有重写onTouchEvent,所以ViewGroup中onTouchEvent()方法就是View中的方法。
(1)事件MotionEvent传递给dispatchTouchEvent(ev)进行事件分发
(2)如果view设置了OnTouchListener,onTouch()会被调用,onTouch()返回false view的onTouchEvent会被调用。onTouch()返回true view的onTouchEvent会被屏蔽掉。

5、setOnTouchListener()\setOnClickListener()\onTouchEvent()调用关系:
下面是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 the event should be handled by accessibility focus first.
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

其中判断了mOnTouchListener != null && mOnTouchListener.onTouch(this, event)条件,之后根据返回结果判断是否去调用onTouchEvent(event)。验证了OnTouchListener 调用优先,并控制onTouchEvent()的调用。
在view的onTouchEvent()方法中

 public boolean onTouchEvent(MotionEvent event) {
        ...代码省略...
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    ...代码省略...
                            if (!focusTaken) {
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClickInternal();
                                }
                            }
                            ...代码省略...
                        }
         ...代码省略...

performClickInternal()方法会调performClick(),贴出performClick()源码

public boolean performClick() {
        // We still need to call this method to handle the cases where performClick() was called
        // externally, instead of through performClickInternal()
        notifyAutofillManagerOnClick();

        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        notifyEnterOrExitForAutoFillIfNeeded(true);

        return result;
    }

这里触发了li.mOnClickListener.onClick(this)。说明点击事件onClick是在onTouchEvent中间接调用的。

6、事件交由上层处理
子视图不能处理事件时候,onTouchEvent 返回false,事件将交给父视图的onTouchEvent调用。如果还不能处理事件则依次往上传递。直到Activity。也就是在getWindow().superDispatchTouchEvent(ev)返回了false。将调用Activity的onTouchEvent()方法。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值