美文网首页
android 中事件传递实现分析

android 中事件传递实现分析

作者: JeremyDai | 来源:发表于2016-06-20 14:56 被阅读51次

    概述

    我们在android 应用开发当中经常会用到onClick、onTouch等事件的处理,但是这个事件是如何分发到我们的View上的呢?或者是如果我们在LinearLayout 中的button或者textview 是谁先收到view的触摸或者点击事件呢?是父view先收到然后分发给你了嘛,今天就学习一下android当中关于事件分发机制。

    同时监听了view的onClick和onTouch哪个消息先收到

    首先需要明确的一点是任何view在触摸的时候首先执行的是view的dispatchTouchEvent 方法,
    比如button ,button的父类是textview,textview的父类是View,在view中有:

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

    可以看到 在判断条件mOnTouchListener.onTouch(this, event)中执行了ontouch的回调,如果我们ontouch返回的false的话,我们的onclick事件就不会得到响应了。而onclick事件的回调就在onTouchEvent方法里面。
    所以根据以上我们知道了 ontouch是优先onclick执行的,如果ontouch返回为true的话就会导致onclick无法执行。

    Viewgroup当中的控件是如何得到触碰消息的。

    首先需要知道viewgroup的概念,大家知道android当中所有的widget都是继承自view的,而viewgroup是包含了好几个view的组合,比如我们经常用到的linearlayout ,LinearLayout、RelativeLayout等都是继承自ViewGroup的,但ViewGroup实际上也是一个View,只不过比起View,它多了可以包含子View和定义布局参数的功能。我们平时用的各种布局都是Vie`wGroup的子类。

    事件传递

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

    里面有个if (disallowIntercept || !onInterceptTouchEvent(ev))的判断,根据这2个条件的值来判断是否进入到里面执行,在 ViewGroup当中onInterceptTouchEvent 默认是返回为false的,你也可以复写这个方法,

    public boolean onInterceptTouchEvent(MotionEvent ev) {  
        return false;  
    }  
    

    返回为true的话,该viewgroup当中的子view将收不到ontouch的事件,因为在for 循环当中会通过一个for循环,遍历了当前ViewGroup下的所有子View,然后判断当前遍历的View是不是正在点击的View,如果是的话就会进入到该条件判断的内部,该View的dispatchTouchEvent,之后就走到第一部分讲的内容里面去了。


    关于view的重绘

    我们重绘view的时候都是调用的invalidate()方法,那看这个方法里应该就有重绘的流程了吧,

    相关文章

      网友评论

          本文标题:android 中事件传递实现分析

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