美文网首页
图解Android的事件分发

图解Android的事件分发

作者: 我是解忧鸭铺鸭 | 来源:发表于2017-09-17 14:59 被阅读0次

    要从源码理解整个事件传递过程,必须先从事件传递的返回值认清整个事件的消费过程,对整个事件有大致的了解,为何事件被消费,为何由子类消费

    需要注意的几个点

    主要这三个方法

    dispatchTouchEvent() —— true消费,false不消费(即为分发);false表示事件停止往下面的视图层级进行传递,同时开始往上面的视图层级的onTouchEvent传递,也称为回溯。
    oninterceptTouchEvent() —— true消费,false不消费(即为不拦截)
    onTouchEvent() —— true消费,false不消费()

    而事件分发一般会经过视图的三个层级:Activity、ViewGroup、View。下表会对视图不同的三个层级拥有的事件分发的相关方法进行整理:

    事件分发相关方法 方法功能 Activity ViewGroup View
    public boolean dispatchTouchEvent 事件分发 + + +
    public boolean onInterceptTouchEvent 事件拦截 +
    public boolean onTouchEvent 事件消费 + + +
    1. view因为没有孩子,没有onInterceptTouchEvent

    2. onTouchEvent中对点击事件的具体处理流程大概如下,只要View的CLICKABLE和LONG_CLICKABLE有一个为true,那么它就会消耗事件,返回true。总的来说,View的可不可用不影响是否消耗事件,只要clickable或者longClickable有一个为true,那么它就会消耗事件。

    3. ViewGroup默认不拦截任何事件,因为从源码中可以看到ViewGroup的onInterceptTouchEvent方法默认返回false.

    4. 由View中dispatchTouchEvent可知,如果一个控件是可点击的,那么点击该控件时,dispatchTouchEvent的返回值必定是true

    public boolean dispatchTouchEvent(MotionEvent event) {  
        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
                mOnTouchListener.onTouch(this, event)) {  
            return true;  
        }  
        return onTouchEvent(event);  
    }
    
    • ViewGroup中dispatchTouchEvent
    public boolean dispatchTouchEvent(MotionEvent ev) {  
        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) {  
                mMotionTarget = null;  
            }  
            if (disallowIntercept || !onInterceptTouchEvent(ev)) {  
                ev.setAction(MotionEvent.ACTION_DOWN);  
                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)) {  
                            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))  {  
                                mMotionTarget = child;  
                                return true;  
                            }  
                        }  
                    }  
                }  
            }  
        }  
        boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||  
                (action == MotionEvent.ACTION_CANCEL);  
        if (isUpOrCancel) {  
            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;  
        }  
        final View target = mMotionTarget;  
        if (target == null) {  
            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 (!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)) {  
            }  
            mMotionTarget = null;  
            return true;  
        }  
        if (isUpOrCancel) {  
            mMotionTarget = null;  
        }  
        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);  
    }
    
    1. 子view的onTouchEvent如果默认false不消费的话,会交给上级的onTouchEvent

    2. viewGroup如果onInterceptTouchEvent不拦截,继续交给子view

    3. viewGroup如果dispatchTouchEvent返回true,消费掉会还给上级的onTouchEvent

    如果上面的点你都清楚的话,说明对事件分发是有清楚认识的,那么接下来上图

    android事件分发图解.png 事件链

    相关文章

      网友评论

          本文标题:图解Android的事件分发

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