大家应该对Android Touch事件分发流程有了大致的了解,其中主要的分发对象包括

Activity:

在设备获取到事件之后首先流转到的既是Activity,如果在所有View都不处理Touch事件的情况下最后也是传回Activity处理

  • 我们先来看Activity中的dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {        if (ev.getAction() == MotionEvent.ACTION_DOWN) {            onUserInteraction();        }        if (getWindow().superDispatchTouchEvent(ev)) {            return true;        }        return onTouchEvent(ev);    }
1.onUserInteraction() 在Activity中为空实现2.getWindow().superDispatchTouchEvent(ev) windows的具体唯一实现PhoneWindow,所以去PhoneWindow查找superDispatchTouchEvent()
  • PhoneWindow.java
    @Override    public boolean superDispatchTouchEvent(MotionEvent event) {        return mDecor.superDispatchTouchEvent(event);    }

mDecor 即为DecorView,DecorView为一个FrameLayout,我们知道FrameLayout父类也为ViewGroup,所以到此就由Activity传到了ViewGroup中去了

public class DecorView extends FrameLayout
  • 最后我们梳理一下Activity传递到ViewGroup的流程:
Created with Raphaël 2.1.0 Activity PhoneWindow DecorView ContentView

ViewGroup:

ViewGroup作为View容器,它主要包括三个部分
1.dispatchTouchEvent( ): 负责分发Touch
2.onInterceptTouchEvent( ): 标记是否拦截Touch事件,如果拦截则自己处理,否则交给子View处理(可以是ViewGroup,也可以是View)
3.onTouchEvent(): 在拦截之后进行事件的处理

首先我们看一下dispatchTouchEvent的流程,源码太长我就不贴上来了

if (actionMasked == MotionEvent.ACTION_DOWN) {          cancelAndClearTouchTargets(ev);          resetTouchState(); }
  • 这里是在对Touch事件初始化,虽然在dispatchTouchEvent中会在最后进行初始化,但是因为在一些异常情况下(app切换,anr等等)并没有进行到初始化的代码,所以在每次touch事件流程开始的时候就再进行一次初始化
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);                } else {                    intercepted = false;                }            } else {                intercepted = true;            }
  • 这里是很关键的一步,用来判定ViewGroup是否拦截事件,从代码我们知道在满足 当前事件为Down 或者 mFirstTouchTarget != null (mFirstTouchTarget即为事件处理View)的情况下才会调用onInterceptTouchEvent(),所以当事件如果为Move并且mFirstTouchTarget == null 的情况下(ViewGroup没有处理当前事件的子View)就直接intercepted = true,表明自己拦截

  • final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0
    用来标志当前ViewGroup能否拦截事件,可以通过requestDisallowInterceptTouchEvent()来改变 mGroupFlags 的值控制

if (!canceled && !intercepted){...}
  • 从if中可以看出在事件没有cancel并且没有被上一步的intercepted所拦截的情况下就会进行mFirstTouchTarget 的相关初始化工作,即遍历能处理这个事件的子View,为什么是能够处理而不是全部呢因为其中有一个判断
if (!canViewReceivePointerEvents(child)          || !isTransformedTouchPointInView(x, y, child, null)) {                   ev.setTargetAccessibilityFocus(false);                   continue;                            }

通过手指的x,y来判断子View是否包含,如果不包含继续遍历,如果包含则加入到mFirstTouchTarget

if (mFirstTouchTarget == null) {                handled = dispatchTransformedTouchEvent(ev, canceled, null,                        TouchTarget.ALL_POINTER_IDS);            } else {                TouchTarget predecessor = null;                TouchTarget target = mFirstTouchTarget;                while (target != null) {                    final TouchTarget next = target.next;                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {                        handled = true;                    } else {                        final boolean cancelChild = resetCancelNextUpFlag(target.child)                                || intercepted;                        if (dispatchTransformedTouchEvent(ev, cancelChild,                                target.child, target.pointerIdBits)) {                            handled = true;                        }                        if (cancelChild) {                            if (predecessor == null) {                                mFirstTouchTarget = next;                            } else {                                predecessor.next = next;                            }                            target.recycle();                            target = next;                            continue;                        }                    }                    predecessor = target;                    target = next;                }            }
  • 接下来 如果不存在mFirstTouchTarget则调用dispatchTransformedTouchEvent,我们看看它的关键代码
if (child == null) {            handled = super.dispatchTouchEvent(transformedEvent);        } else {            final float offsetX = mScrollX - child.mLeft;            final float offsetY = mScrollY - child.mTop;            transformedEvent.offsetLocation(offsetX, offsetY);            if (! child.hasIdentityMatrix()) {                transformedEvent.transform(child.getInverseMatrix());            }            handled = child.dispatchTouchEvent(transformedEvent);        }

1.mFirstTouchTarget== null 的情况下,即child == null,就会调用super.dispatchTouchEvent( ),我们知道ViewGroup的父类为View,所以就到了View中了,具体View的dispatchTouchEvent( )下面会介绍
2.mFirstTouchTarget!= null 的情况下,即child != null,就调用子类本生的dispatchTouchEvent( )

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;            }            //onTouchEvent()            if (!result && onTouchEvent(event)) {                result = true;            }        }

所以我们看到了onTouchEvent()就是在此刻调用的,能够成功调用的前提是mOnTouchListener 在不为空的情况下并且onTouch必须返回false,否则不能到onTouchEvent( )中

  • 最后我总结一下整个流程(在我们没有进行任何控制的情况下),其中包含关系为Group1->Group2->View

  • 另外一种情况Group1->Group2

更多相关文章

  1. Android(安卓)Mms之短信接收流程--从Framework到App
  2. VelocityTracker使用详解
  3. Android(安卓)Activity原理以及其子类描述
  4. 记录 Android(安卓)WebView 开发过程的坑和解决方法
  5. 浅谈Android下基于brodcom4330的FM驱动
  6. Android中的事件传递
  7. TMTS 简介
  8. Android(安卓)自定义View的构造函数详细介绍
  9. Android应用程序键盘(Keyboard)消息处理机制分析(三)

随机推荐

  1. SQL Server2012在开发中的一些新特性
  2. 高效的SQLSERVER分页查询(推荐)
  3. 真正高效的SQLSERVER分页查询(多种方案)
  4. SSB(SQLservice Service Broker) 入门实
  5. sql server 中合并某个字段值的实例
  6. 数据库中两张表之间的数据同步增加、删除
  7. SQL Server数据库中批量导入数据的四种方
  8. sqlserver数据库出现置疑的解决思路
  9. MSSQL 2005/2008 日志压缩清理方法小结
  10. SQL Server游标的使用/关闭/释放/优化小