个人博客:http://zhangsunyucong.top

android事件的源头在哪里?

当用户触摸屏幕或者按键等时,形成事件,事件经过linux底层Event节点捕获之后,一直传到android应用层。中间传递的过程不是本文的重点,我也不是很清楚(哈哈哈)。本文的重点是事件在应用层的分发机制。

事件在View树中的分发过程

View树:

在Android中,事件的分发过程就是MotionEvent在view树分发的过程。默认是中从上而下,然后从下而上的传递的,直到有view、viewgroup或者Activity处理事件为止。

为什么要先从上而下?是为了在默认情况下,屏幕上层叠的所有控件都有机会处理事件。这个阶段我们称为事件下发阶段。

为什么要从下而上?是为了在从上而下分发时,事件没有控件处理时,再从下而上冒泡事件,是否有控件愿意处理事件。如果中间没有控件处理,事件就只能由Acitivity处理了。这个阶段我们称为事件的冒泡阶段。

准备

事件序列:从用户手指触摸屏幕开始,经过滑动到手指离开屏幕。这个操作产生了一个dowm事件,一系列move事件,最后一个up事件结束。我们把这一个操作产生的事件称为一个事件序列。

Acitivity中和事件传递有关的函数
事件分发:dispatchTouchEvent
事件处理:onTouchEvent

ViewGrop中和事件传递有关的函数
事件分发:dispatchTouchEvent
事件拦截:onInterceptTouchEvent
事件处理:onTouchEvent

View中和事件传递有关的函数
事件分发:dispatchTouchEvent
事件处理:onTouchEvent

从上面可以看出,ViewGrop中多了事件拦截onInterceptTouchEvent函数,是为了询问自己是否拦截事件(在事件分发中询问),如果没有拦截就传递事件给直接子view,如果拦截就将事件交给自己的事件处理函数处理。View中没有事件拦截函数,因为view是在view树中的叶节点,已经没有子view。

下面是先进行源码分析,然后再验证得出一些结论。代码迟点上传github。
用图表示布局的层级关系:

这里分析事件的分发过程,是从down事件的分发开始,以及分析它在两个阶段的传递过程:下发阶段和冒泡阶段。

事件下发阶段

(1)在Acitvity中的源码分析:

Activity#dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {        if (ev.getAction() == MotionEvent.ACTION_DOWN) {            onUserInteraction();        }        if (getWindow().superDispatchTouchEvent(ev)) {            return true;        }        return onTouchEvent(ev);}

在第4行,Acivity将事件传递给了Window,Window是一个抽象类。在手机系统中它的实现是PhoneWindow.下面进入PhoneWindow中。

PhoneWindow#superDispatchTouchEvent

@Overridepublic boolean superDispatchTouchEvent(MotionEvent event) {    return mDecor.superDispatchTouchEvent(event);}

从上面可以看出,事件已经从Acitivity到PhoneWindow,再传到了DecorView。DecorView是一个继承FrameLayout的ViewGroup,从而事件进入了View树的传递。

重写在Acitvity中的事件传递方法

重写Activity#dispatchTouchEvent:
1、返回false,事件不分发,所有事件在Acitivity的分发函数中就中断(真的不见了),连Acitivity的事件处理函数都到达不了。
2、返回true,所有事件在Acitivity的分发函数中就中断,和false一样
3、返回父函数方法,事件就传给直接子view分发

(2)在ViewGruop中的源码分析:
ViewGruop#dispatchTouchEvent

final int action = ev.getAction();final int actionMasked = action & MotionEvent.ACTION_MASK;// Handle an initial down.if (actionMasked == MotionEvent.ACTION_DOWN) {    // Throw away all previous state when starting a new touch gesture.    // The framework may have dropped the up or cancel event for the previous gesture    // due to an app switch, ANR, or some other state change.    cancelAndClearTouchTargets(ev);    resetTouchState();}// Check for interception.final boolean intercepted;if (actionMasked == MotionEvent.ACTION_DOWN    || mFirstTouchTarget != null) {        final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;        if (!disallowIntercept) {            intercepted = onInterceptTouchEvent(ev);            ev.setAction(action); // restore action in case it was changed        } else {            intercepted = false;        }} else {    // There are no touch targets and this action is not an initial down    // so this view group continues to intercept touches.    intercepted = true;}

在5-11行,是每个新的事件系列开始前,会重置事件相关的状态。这里我们关注两个地方。第一个是第17行的disallowIntercept标志,第二个是第19行调用了事件拦截函数,询问是否拦截事件。

ViewGruop#onInterceptTouchEvent

public boolean onInterceptTouchEvent(MotionEvent ev) {        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)                && ev.getAction() == MotionEvent.ACTION_DOWN                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)                && isOnScrollbarThumb(ev.getX(), ev.getY())) {            return true;        }        return false;}

onInterceptTouchEvent的代码很简单。

重写在ViewGroup中的事件传递方法
重写ViewGroup#dispatchTouchEvent:
1、返回false,不分发,down事件给父ViewGroup处理,以后的事件全部直接通过父ViewGroup分发函数给父ViewGroup的事件处理函数处理。
2、返回true,则所有的事件都从头来到这里就中断,不见了。
3、返回父函数方法,看下面拦截函数

重写ViewGroup#onInterceptTouchEvent(询问是否拦截):
1、返回true,就调用处理函数,在处理函数中是否消耗down事件
2、返回false,是否是最后一个view?否,down事件就分发给子View;是,就调用一次它的处理函数,进入冒泡阶段(就是一寸事件处理函数调用)
3、返回父函数的方法,和返回false一样

重写ViewGroup的onTouchEvent,当down事件来到中onTouchEvent时,
1、返回true,就消耗down事件,后面全部事件从头分发到处理函数(不用再询问是否拦截)。后面的事件根据是否消耗而是否消失(不消耗就消失),消失的所有事件由Acitivity处理(注意消失的事件也是从头传递到这里再传给Acitivity的)。
2、返回false,将down事件冒泡回去,看谁会处理。
3、返回父函数方法,是默认不消耗。

(3)在View中的源码分析:
View#dispatchTouchEvent

if (onFilterTouchEventForSecurity(event)) {    if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {        result = true;    }    //noinspection SimplifiableIfStatement    ListenerInfo li = mListenerInfo;    if (li != null && li.mOnTouchListener != null        && (mViewFlags & ENABLED_MASK) == ENABLED            && li.mOnTouchListener.onTouch(this, event)) {                result = true;    }    if (!result && onTouchEvent(event)) {        result = true;    }}

这里关注的地方是,第9行和第13行。第9行是当前view如果设置了onTouch事件,并且它返回了true,那它就直接将result设置为true,事件就消耗了,不会再继续传递下去,只到达onTouch。第13行,是事件处理函数。可以看出onTouch是优先于onTouchEvent的。

View#onTouchEvent

....final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;...                if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {    switch (action) {        case MotionEvent.ACTION_UP:        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;        if ((viewFlags & TOOLTIP) == TOOLTIP) {            handleTooltipUp();        }        if (!clickable) {            removeTapCallback();            removeLongPressCallback();            mInContextButtonPress = false;            mHasPerformedLongPress = false;            mIgnoreNextUpEvent = false;            break;        }        boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;        if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {            // take focus if we don't have it already and we should in            // touch mode.            boolean focusTaken = false;            if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {                focusTaken = requestFocus();            }            if (prepressed) {                // The button is being released before we actually                // showed it as pressed.  Make it show the pressed                // state now (before scheduling the click) to ensure                // the user sees it.                setPressed(true, x, y);            }            if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {                // This is a tap, so remove the longpress check                removeLongPressCallback();                // Only perform take click actions if we were in the pressed state                if (!focusTaken) {                    // Use a Runnable and post this rather than calling                    // performClick directly. This lets other visual state                    // of the view update before click actions start.                    if (mPerformClick == null) {                        mPerformClick = new PerformClick();                    }                    if (!post(mPerformClick)) {                        performClick();                    }                }            }            ...        }        ...    }    return true;}

view根据是否可以点击等等一系列判断什么的。这里关注up事件中的第42-53行,有performClick。

View#performClick

public boolean performClick() {    final boolean result;    final ListenerInfo li = mListenerInfo;    if (li != null && li.mOnClickListener != null) {        playSoundEffect(SoundEffectConstants.CLICK);        li.mOnClickListener.onClick(this);        result = true;    } else {        result = false;    }    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);    notifyEnterOrExitForAutoFillIfNeeded(true);    return result;}

如果view设置了mOnClickListener,即点击事件,会调用view的点击事件。如果在父view中拦截了up事件,使up事件到达不了这里,会使view的点击事件失效。

可以知道,onTouch是优先于onTouchEvent,onTouchEvent优先于onclick。

事件冒泡阶段

当down事件到达了最后一个子view,如果仍然没有view愿意处理它,就调用一次最后一个子view的事件处理函数,是否处理dowm事件,如果不处理,就一直冒泡回去,直到有view的onTouchEvent处理为止。如果都不处理,就只有Acitivity自己处理了。整个事件冒泡阶段就是一串onTouchEvent的回溯过程,自下而上。

更多相关文章

  1. Android下的图形处理
  2. 【Android】ViewPager实现图片左右滑动播放及添加点击事件
  3. 我的Android进阶之旅------>Android中解析XML 技术详解---->SAX
  4. Android中几种图像特效处理的集锦!!!
  5. Android(安卓)Touch 触摸事件
  6. 基于Eclipse的Android(安卓)JNI层测试应用开发过程记录
  7. Android核心分析(17) ------电话系统之rilD
  8. Android自动化测试工具——Monkey
  9. 箭头函数的基础使用

随机推荐

  1. Android中MediaPlayer的setDataSource方
  2. Fragment的使用简介【Android】
  3. Android错误集
  4. android write file
  5. Android(安卓)深入研究SQLite实例(二)
  6. android 使图片显示 圆角
  7. Android操作系统11种传感器介绍
  8. Android获取Manifest中元素的值
  9. android用intent打开各种文件
  10. 【转】Android(安卓)studio2.2.3 支持Jav