注 : 网上关于安卓事件分发的文章多如牛毛,本文仅基于自己的理解,可能不够深入,刚写博客,很多地方写不好,不喜勿喷。

首先,事件从Activity中开始分发。

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

这里面getWindow()就是Activity的PhoneWindow,进去这个类看下:

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

这个mDecor其实就是DecorView,它是PhoneWindow的顶层ViewGroup,我们Activity中加载的视图都是它的子View,下面我们进去ViewGroup的dispatchTouchEvent看下其实现,这个方法稍微有点长。

 @Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onTouchEvent(ev, 1);}// If the event targets the accessibility focused view and this is it, start// normal event dispatch. Maybe a descendant is what will handle the click.if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {ev.setTargetAccessibilityFocus(false);}**//是否处理标志位**boolean handled = false;if (onFilterTouchEventForSecurity(ev)) {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;}// If intercepted, start normal event dispatch. Also if there is already// a view that is handling the gesture, do normal event dispatch.if (intercepted || mFirstTouchTarget != null) {ev.setTargetAccessibilityFocus(false);}// Check for cancelation.final boolean canceled = resetCancelNextUpFlag(this)|| actionMasked == MotionEvent.ACTION_CANCEL;// Update list of touch targets for pointer down, if needed.final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;TouchTarget newTouchTarget = null;boolean alreadyDispatchedToNewTouchTarget = false;if (!canceled && !intercepted) {// If the event is targeting accessiiblity focus we give it to the// view that has accessibility focus and if it does not handle it// we clear the flag and dispatch the event to all children as usual.// We are looking up the accessibility focused host to avoid keeping// state since these events are very rare.View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()? findChildWithAccessibilityFocus() : null;if (actionMasked == MotionEvent.ACTION_DOWN|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {final int actionIndex = ev.getActionIndex(); // always 0 for downfinal int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex): TouchTarget.ALL_POINTER_IDS;// Clean up earlier touch targets for this pointer id in case they// have become out of sync.removePointersFromTouchTargets(idBitsToAssign);final int childrenCount = mChildrenCount;if (newTouchTarget == null && childrenCount != 0) {final float x = ev.getX(actionIndex);final float y = ev.getY(actionIndex);// Find a child that can receive the event.// Scan children from front to back.final ArrayList<View> preorderedList = buildTouchDispatchChildList();final boolean customOrder = preorderedList == null&& isChildrenDrawingOrderEnabled();final View[] children = mChildren;for (int i = childrenCount - 1; i >= 0; i--) {final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);// If there is a view that has accessibility focus we want it// to get the event first and if not handled we will perform a// normal dispatch. We may do a double iteration but this is// safer given the timeframe.if (childWithAccessibilityFocus != null) {if (childWithAccessibilityFocus != child) {continue;}childWithAccessibilityFocus = null;i = childrenCount - 1;}if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {ev.setTargetAccessibilityFocus(false);continue;}newTouchTarget = getTouchTarget(child);if (newTouchTarget != null) {// Child is already receiving touch within its bounds.// Give it the new pointer in addition to the ones it is handling.newTouchTarget.pointerIdBits |= idBitsToAssign;break;}resetCancelNextUpFlag(child);if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {// Child wants to receive touch within its bounds.mLastTouchDownTime = ev.getDownTime();if (preorderedList != null) {// childIndex points into presorted list, find original indexfor (int j = 0; j < childrenCount; j++) {if (children[childIndex] == mChildren[j]) {mLastTouchDownIndex = j;break;}}} else {mLastTouchDownIndex = childIndex;}mLastTouchDownX = ev.getX();mLastTouchDownY = ev.getY();newTouchTarget = addTouchTarget(child, idBitsToAssign);alreadyDispatchedToNewTouchTarget = true;break;}// The accessibility focus didn't handle the event, so clear// the flag and do a normal dispatch to all children.ev.setTargetAccessibilityFocus(false);}if (preorderedList != null) preorderedList.clear();}if (newTouchTarget == null && mFirstTouchTarget != null) {// Did not find a child to receive the event.// Assign the pointer to the least recently added target.newTouchTarget = mFirstTouchTarget;while (newTouchTarget.next != null) {newTouchTarget = newTouchTarget.next;}newTouchTarget.pointerIdBits |= idBitsToAssign;}}}// Dispatch to touch targets.if (mFirstTouchTarget == null) {// No touch targets so treat this as an ordinary view.handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);} else {// Dispatch to touch targets, excluding the new touch target if we already// dispatched to it.  Cancel touch targets if necessary.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;}}// Update list of touch targets for pointer up or cancel, if needed.if (canceled|| actionMasked == MotionEvent.ACTION_UP|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {resetTouchState();} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {final int actionIndex = ev.getActionIndex();final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);removePointersFromTouchTargets(idBitsToRemove);}}if (!handled && mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);}return handled;}

1、 final boolean intercepted;设立拦截标志位。
2、 if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null)
如果是ACTION_DOWN事件或者mFirstTouchTarget 不为空进入是否拦截判断,如果我们DOWN事件已经传下去找到接收Touch事件的view,mFirstTouchTarget 不为空,待会我们往下看就知道了。
3、final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;子View设置父View不允许拦截,通过调用requestDisallowInterceptTouchEvent进行设置。
4、if (!canceled && !intercepted) {….}如果事件移出View坐标外以及不拦截的话,会进入该判断内部,进行目标View查找。
5、final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i–) {….}
循坏遍历子View,寻找满足条件的view.

     if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {ev.setTargetAccessibilityFocus(false);continue;}

我们看到有两个条件,首先canViewReceivePointerEvents从方法名就看出来,判断是否可以接收Touch事件,我们看下。

private static boolean canViewReceivePointerEvents(@NonNull View child) {return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE|| child.getAnimation() != null;}

如果child 可见或者有动画满足条件。
isTransformedTouchPointInView()方法是判断Touch事件坐标是否在该View有效内部。

6、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);}

我们看到此时会调用child的dispatchTouchEvent方法,如果dispatchTouchEvent返回true,那么dispatchTransformedTouchEvent也就return true,此时会走到 newTouchTarget = addTouchTarget(child, idBitsToAssign);

 private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);target.next = mFirstTouchTarget;mFirstTouchTarget = target;return target;}

这个时候mFirstTouchTarget 就有值了,接着往下看。

7、如果我们没有找到或者child不处理事件呢,那么mFirstTouchTarget为null,我们看方法是怎么处理的:

 // Dispatch to touch targets.if (mFirstTouchTarget == null) {// No touch targets so treat this as an ordinary view.handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);} 

注释也说的很清楚了,如果没有找到,父ViewGroup就当成普通的View处理Touch事件。
8、如果都不处理,那么整个入口的dispatchTouchEvent会返回false,交给Activity处理。

ViewGroup 事件分发相关推荐

  1. Android ViewGroup事件分发机制

    理~ 1.案例 首先我们接着上一篇的代码,在代码中添加一个自定义的LinearLayout: [java] view plaincopy package com.example.zhy_event03 ...

  2. android viewgroup点击变色,Android ViewGroup事件分发

    上篇文章已经分析了Android的Touch事件分发.如果没看的建议先看一下.Android View的Touch事件分发. 接下来我们开始写几种场景,得出一个初步的执行顺序,然后我们按照这个顺序开始 ...

  3. 红橙Darren视频笔记 ViewGroup事件分发分析 基于API27

    本节目标,通过案例,先看程序运行结果,然后跟踪源码,理解为什么会有这样的输出,继而理解view group的分发机制,感觉和证明题很像呢. 考虑以下程序的运行结果: case1: public cla ...

  4. 对Android view/viewgroup事件分发的理解

    首先看看讲事件分发的博客: http://blog.csdn.net/xiaanming/article/details/21696315 和 http://www.csdn123.com/html/ ...

  5. View及ViewGroup的事件分发及传递(二)

     在上一篇文章中说到的是关于View的事件分发机制,这篇文章就谈一谈关于ViewGroup的事件分发机制: 关于ViewGroup的事件分发机制,一般情况下,和View的情况是大致一样的,只不过由 ...

  6. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 七 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  7. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 六 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  8. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 五 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  9. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 三 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

最新文章

  1. 人群密度估计--CNN-based Cascaded Multi-task Learning of High-level Prior and Density Estimation for Crowd
  2. IT绩效管理消除IT与业务之间的隔阂
  3. 关于Ajax 错误:'sys'未定义解决方法.
  4. wordpress--SEO们的福音
  5. 汇编语言的码制转换小问题--求指教
  6. 【王俊杰de人工智能实战课】第7次作业2
  7. 【C语言】单链表的相关热点面试题(包括:从尾到头打印,逆置,冒泡,寻找中间节点,倒数k节点)
  8. 中南大学12月13日考c语言,中南大学2010级C语言试卷
  9. Kaggle比赛(二)House Prices: Advanced Regression Techniques
  10. “好烦!自拒修电脑后,妹子又约我学机器学习,咋办?”
  11. 1月13 PyTorch 中模型的使用,保存加载模型
  12. 错落有致——集团企业信息化规划原则
  13. linux eclipse svn插件安装,Ubuntu16.04 Eclipse 安装 SVN 插件 subclipse 时 JavaHL 报错解决...
  14. 请启封,您的双态IT大会邀请函
  15. python不好找工作怎么办信用卡_利用python进行信用卡欺诈检测
  16. 使用fastdfs上传视频并使用FFmpegFrameGrabberFrameGrabber在上传视频时截图作为封面
  17. Java中的自动向量化(SIMD)
  18. 2020汽车驾驶员(中级)证考试及汽车驾驶员(中级)操作证考试
  19. 计步器(Pedometer)实现原理简介
  20. 2018年Android面试题含答案--适合中高级

热门文章

  1. KAWAI 钢琴编号、年代、型号表
  2. 安装Microsoft Visual Studio 2008的时候,经常会出现Microsoft Visual Studio Web创作组件安装失败的情况
  3. 2006年四种思路帮你创业
  4. CSS也能控制表格的交替颜色
  5. 为什么中国学生会越学越呆?
  6. 国家广电总局:常规电视剧剧集正片时长不少于41分钟
  7. 比亚迪:华为的手机,我们造的
  8. 变了,iPhone 12变身iPhone 4模样;下一代只支持单种5G频段?
  9. iPhone新机或全部采用OLED屏:日本JDI股价应声下跌
  10. 刘强东夫妇向英国捐赠大量防疫物资:在英华侨及留学生可免费认领