• 事件分发机制


    事件分发

    分发对象

    事件

    事件种类

    • MotionEvent.ACTION_DOWN:按下View(所有事件的开始)
    • MotionEvent.ACTION_MOVE:滑动View
    • MotionEvent.ACTION_CANCEL:非人为原因结束本次事件
    • MotionEvent.ACTION_UP:抬起View(与DOWN对应)

    事件流程

    image

    事件分发本质

    当一个点击事件发生后,系统要讲该事件传递至具体的view进行处理的过程,这个传递的过程,就是事件分发的本质

    传递方向

    从activity到phonewindow到decorview再到开发者的viewgroup,view

    响应方向

    以传递相反的方向进行响应

    如何传递

    image

    方法作用

    image
    一个事件列中,只要viewgroup中的onInterceptTouchEvent返回值有一次为true,后续事件列将不会在走onIntercepetTouchEvent,直接进入viewgroup中的onTouchEvent中。在move中的intercepet,该move事件会被当做cancel,传递view中。

    Activity分发机制

    public boolean dispatchTouchEvent(MotionEvent ev) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                onUserInteraction();
            }
            if (getWindow().superDispatchTouchEvent(ev)) {
                return true;
            }
            return onTouchEvent(ev);
        }
    
    • 一般事件列开始都是DOWN(按下按钮),所以这里返回true,执行onUserInteraction()
    • onUserInteraction()源码为空,当此activity在栈顶时,触屏点击按home,back,menu键等都会触发此方法。onUserInteraction()主要用于屏保
    • Window类是抽象类,且PhoneWindow是Window类的唯一实现类,即执行phoneWindow中的superDispatchTouchEvent。
     @Override
        public boolean superDispatchTouchEvent(MotionEvent event) {
            return mDecor.superDispatchTouchEvent(event);
        }
    

    mDecor为DecorView的事例,即执行DecorView中的superDispatchTouchEvent。

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

    DecorView继承FrameLayout,即执行ViewGroup中的dispatchTouchEvent。这样事件就进入了viewgroup中。

    ViewGroup分发机制

    @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (!onFilterTouchEventForSecurity(ev)) {
                return false;
            }
    
            final int action = ev.getAction();
            final float xf = ev.getX();
            final float yf = ev.getY();
            final float scrolledXFloat = xf + mScrollX;
            final float scrolledYFloat = yf + mScrollY;
            final Rect frame = mTempRect;
    
            boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    
            if (action == MotionEvent.ACTION_DOWN) {
                if (mMotionTarget != null) {
                    // this is weird, we got a pen down, but we thought it was
                    // already down!
                    // XXX: We should probably send an ACTION_UP to the current
                    // target.
                    mMotionTarget = null;
                }
                // If we're disallowing intercept or if we're allowing and we didn't
                // intercept
                if (disallowIntercept || !onInterceptTouchEvent(ev)) {
                    // reset this event's action (just to protect ourselves)
                    ev.setAction(MotionEvent.ACTION_DOWN);
                    // We know we want to dispatch the event down, find a child
                    // who can handle it, start with the front-most child.
                    final int scrolledXInt = (int) scrolledXFloat;
                    final int scrolledYInt = (int) scrolledYFloat;
                    final View[] children = mChildren;
                    final int count = mChildrenCount;
    
                    for (int i = count - 1; i >= 0; i--) {
                        final View child = children[i];//*
                        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
                                || child.getAnimation() != null) {
                            child.getHitRect(frame);
                            if (frame.contains(scrolledXInt, scrolledYInt)) {
                                // offset the event to the view's coordinate system
                                final float xc = scrolledXFloat - child.mLeft;
                                final float yc = scrolledYFloat - child.mTop;
                                ev.setLocation(xc, yc);
                                child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
                                if (child.dispatchTouchEvent(ev))  {
                                    // Event handled, we have a target now.
                                    mMotionTarget = child;
                                    return true;
                                }
                                // The event didn't get handled, try the next view.
                                // Don't reset the event's location, it's not
                                // necessary here.
                            }
                        }
                    }
                }
            }
    
            boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
                    (action == MotionEvent.ACTION_CANCEL);
    
            if (isUpOrCancel) {
                // Note, we've already copied the previous state to our local
                // variable, so this takes effect on the next event
                mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
            }
    
            // The event wasn't an ACTION_DOWN, dispatch it to our target if
            // we have one.
            final View target = mMotionTarget;//*
            if (target == null) {
                // We don't have a target, this means we're handling the
                // event as a regular view.
                ev.setLocation(xf, yf);
                if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
                }
                return super.dispatchTouchEvent(ev);
            }
    
            // if have a target, see if we're allowed to and want to intercept its
            // events
            if (!disallowIntercept && onInterceptTouchEvent(ev)) {
                final float xc = scrolledXFloat - (float) target.mLeft;
                final float yc = scrolledYFloat - (float) target.mTop;
                mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
                ev.setAction(MotionEvent.ACTION_CANCEL);
                ev.setLocation(xc, yc);
                if (!target.dispatchTouchEvent(ev)) {
                    // target didn't handle ACTION_CANCEL. not much we can do
                    // but they should have.
                }
                // clear the target
                mMotionTarget = null;
                // Don't dispatch this event to our own view, because we already
                // saw it when intercepting; we just want to give the following
                // event to the normal onTouchEvent().
                return true;
            }
    
            if (isUpOrCancel) {
                mMotionTarget = null;
            }
    
            // finally offset the event to the target's coordinate system and
            // dispatch the event.
            final float xc = scrolledXFloat - (float) target.mLeft;
            final float yc = scrolledYFloat - (float) target.mTop;
            ev.setLocation(xc, yc);
    
            if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
                ev.setAction(MotionEvent.ACTION_CANCEL);
                target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
                mMotionTarget = null;
            }
    
            return target.dispatchTouchEvent(ev);
        }
    

    View分发机制

    public boolean dispatchTouchEvent(MotionEvent event) {
            if (!onFilterTouchEventForSecurity(event)) {
                return false;
            }
    
            if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
                    mOnTouchListener.onTouch(this, event)) {
                return true;
            }
            return onTouchEvent(event);
        }
    

    小细节

    • 为啥viewgroup中的onInterceptTouchEvent在一个事件列中,只走一次?如果viewgroup中的子view不消费事件,就会这样。因为在事件列后续的move、cancel、up等中,viewgroup被当做view执行了。
  • 相关阅读:
    使用Microsoft URL Rewrite Module for IIS 7.0修改WEB.CONFIG即可实现*.HTML伪静态 无需修改应用程序映射
    我的第二个智能手机 HKC G801 严重质量问题 USB接口坏后解决办法
    JQuery的跨域方法 可跨新浪、腾讯等任意网站
    IIS7下使用MSDNURLRewriting.msi开源组件的设置
    面向对象编程,真离不开设计模式?
    jQuery 的filter(),not(),split()用法
    2008年总结
    程序中 同步和异步的经典解释 (一听就懂)
    2009年元旦前祝福 开心一下
    到底什么是SOA?
  • 原文地址:https://www.cnblogs.com/qinhe/p/6371030.html
Copyright © 2020-2023  润新知