android 中事件传递实现分析

2016-06-20  本文已影响51人  JeremyDai

概述

我们在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()方法,那看这个方法里应该就有重绘的流程了吧,

上一篇下一篇

猜你喜欢

热点阅读