zoukankan      html  css  js  c++  java
  • 事件分发机制

    事件分发

    分发对象

    事件

    事件种类

    • 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执行了。
  • 相关阅读:
    高德地图 Android编程中 如何设置使 标记 marker 能够被拖拽
    Android编程 高德地图 中如何重写 定位按键 的触发事件 (com.amap.api.maps2d.LocationSource)点击定位后不仅定位在地图中心点上而且可以设置地图的缩放大小和提示
    Android 编程 高德地图 (实现显示地图以及定位功能)
    Android 编程 AMapLocationClientOption 类中的 setMockEnable (高德地图 com.amap.api.location.AMapLocationClientOption 中的类)
    Android 编程 AMapLocationClientOption 类中的 setNeedAddress 方法用处 (高德地图 com.amap.api.location.AMapLocationClientOption 中的类)
    数据挖掘、目标检测中的cnn和cn---卷积网络和卷积神经网络
    mfc学习之gdi---gdi空间
    图像处理之特效---光剑特效
    模式识别之线性回归---最小二乘和线性回归
    matlab 学习之常用函数2
  • 原文地址:https://www.cnblogs.com/qinhe/p/6371030.html
Copyright © 2011-2022 走看看