Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考

View的Touch事件分发(一.初步了解) View的Touch事件分发(一.初步了解)_yinianzhijian99的专栏-CSDN博客

View的Touch事件分发(二.源码分析) View的Touch事件分发(二.源码分析)_yinianzhijian99的专栏-CSDN博客

现在开始分析ViewGroup的Touch事件分发

现象:给一个控件设置OnTouchListener和OnClickListener,点击控件,会有以下结果:

1.正常情况下

手指按下

DOWN ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent ->

View.dispatchTouchEvent -> View.onTouch -> View.onTouchEvent

手指移动

MOVE ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent ->

View.dispatchTouchEvent -> View.onTouch -> View.onTouchEvent

手指抬起

UP ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent ->

View.onTouch -> View.onTouchEvent -> View.onclick

2.onClick没有,理解为没有消费事件

ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -

> View.dispatchTouchEvent -> View.onTouch -> View onTouchEvent -

> ViewGroup.onTouchEvent

3.在View的onTouchEvent()方法里面返回true的情况下

手指按下

DOWN ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent ->

View.dispatchTouchEvent -> View.onTouch -> View.onTouchEvent

手指移动

MOVE ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent ->

View.dispatchTouchEvent -> View.onTouch -> View.onTouchEvent

手指抬起

UP ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent ->

View.onTouch -> View.onTouchEvent

4.在ViewGroup的onInterceptTouchEvent()方法里面返回true的情况下

ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -> ViewGroup.onTouchEvent

源码分析:

ViewGroup的Touch事件是从Activity的dispatchTouchEvent()方法开始的

Activity的dispatchTouchEvent()方法如下:

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

getWindow()是mWindow,mWindow = new PhoneWindow(this);

进入PhoneWindow的superDispatchTouchEvent()方法:

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

DecorView继承了FrameLayout,最终会调用ViewGroup的dispatchTouchEvent()方法:

@Override
public 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.//对一些变量重置,清除TouchTargets,mFirstTouchTarget = nullcancelAndClearTouchTargets(ev);resetTouchState();}// Check for interception.//检查是否需要拦截touch事件//mFirstTouchTarget是ViewGroup的一个内部类封装了被触摸的view//mFirstTouchTarget不为空表示有子View消费touch事件//mFirstTouchTarget为空表示ViewGroup拦截了touch事件或者子view没有消费touch事件final boolean intercepted;if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (!disallowIntercept) {//根据onInterceptTouchEvent()方法的返回值来决定是否拦截touch事件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.//如果不是ACTION_DOWN并且没有子view消费掉touch事件,拦截touch事件//如果ACTION_DOWN没有被子view消费,//那么接下来的ACTION_MOVE,ACTINO_UP就都不会走onInterceptTouchEvent()方法//将intercepted设置为true直接进行拦截交给自身进行touch事件的处理(dispatchTouchEvent() -> onTouch() -> onTouchEvent())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.//检查cancel状态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) {计算touch点的坐标根据touch点坐标判断当前触摸到的是哪个子viewfinal 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 = buildOrderedChildList();final boolean customOrder = preorderedList == null&& isChildrenDrawingOrderEnabled();final View[] children = mChildren;注意是从后往前进行遍历 最后面的child是最上层的viewfor (int i = childrenCount - 1; i >= 0; i--) {final int childIndex = customOrder? getChildDrawingOrder(childrenCount, i) : i;final View child = (preorderedList == null)? children[childIndex] : preorderedList.get(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;//已经找到了接收touch事件的子view,不用再继续遍历break;}resetCancelNextUpFlag(child);//dispatchTransformedTouchEvent()中调用了child.dispatchTouchEvent()方法//根据child.dispatchTouchEvent()方法的返回值判断子view是否消费了touch事件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);//为true表示此时已经将touch事件分发给了子view,即子view消费了touch事件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.//在ACTION_UP手指抬起或者ACTION_CANCEL取消touch事件分发,恢复重置一些状态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;
}

整个touch事件的传递过程为: Activity.dispatchTouchEvent() -> PhoneWindow.superDispatchTouchEvent() -> DecorView.superDispatchTouchEvent() -> ViewGroup.dispatchTouchEvent() -> View.dispatchTouchEvent()

而消费过程则相反: View.onTouchEvent() -> ViewGroup.onTouchEvent() -> DecorView.onTouchEvent() -> Activity.onTouchEvent()

如果子View没有一个地方返回true,touch事件只会进来一次,只会响应DOWN事件,代表不需要消费该事件,如果你想响应MOVE或UP必须找个地方ture。

对于ViewGroup来讲,如果你想拦截子View的Touch事件,可以覆写onInterceptTouchEvent,返回true即可 。

如果onInterceptTouchEvent返回的是true,会执行该ViewGroup的onTouchEvent方法 , 如果子View没有消费touch事件,也会调用该ViewGroup的onTouchEvent 方法。

ViewGroup的Touch事件分发(源码分析)相关推荐

  1. Android Touch事件分发(源码分析)

    Android一文让你轻松搞定Touch事件分发 源码分析 下面,咱们一起通过源码,全面解析事件分发机制,即按顺序讲解: Activity事件分发机制 ViewGroup事件分发机制 View事件分发 ...

  2. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )

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

  3. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 )

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

  4. Flutter事件响应源码分析

    Flutter作为一个UI框架,本身也有自己的事件处理方式,本文主要阐述触摸事件从native传递到Flutter后是如何被widget识别以及分发的.至于native系统是如何监听触摸事件以及传递事 ...

  5. Qt 事件机制源码分析 QApplication exec 源码分析 多图超级详细

    前言: 不熟悉qt 源码结构的 可以先看这一篇 点我点我点我 写qt 的都知道 以下代码, 这段代码究竟的运行机制是怎么样的 咱们一步一步的看 QApplication a(argc, argv);Q ...

  6. memcached(二)事件模型源码分析

    在memcachedd中,作者为了专注于缓存的设计,使用了libevent来开发事件模型.memcachedd的时间模型同nginx的类似,拥有一个主进行(master)以及多个工作者线程(woker ...

  7. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  8. Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制

    转载请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/21696315),请尊重他人的辛勤劳动成果,谢谢! 今天这篇 ...

  9. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

最新文章

  1. linux tcp窗口大小设置,高性能Linux:TCP/IP内核参数调优之TCP窗口扩大因子(TCP Window Scaling)选项(理论篇)...
  2. webstorm github怎么用_前端开发神器WebStorm发布最新版本2019.3,代码完成更加智能...
  3. git中找回丢失的对象
  4. 【网络安全】某安全网关前端JS分析
  5. 如何只使用标签来构建一个简单的电影推荐系统
  6. python中list是什么类型_Python 入门系列 —— 13. List 类型简介
  7. It's all about buffers: zero-copy, mmap and Java NIO
  8. Struts2中过滤器,拦截器,监听器他们之间有什么区别?
  9. radio 取值赋值 亲测有用实效
  10. UE4 Roadmap
  11. TensorFlow版本的HelloWord
  12. 从芯片到 AI 生态,52 岁英特尔的蜕变!
  13. Linux服务器时间同步那些事
  14. appium手机键盘实现方法
  15. 分享117个PHP源码,总有一款适合你
  16. python全栈工程师视频_python全栈工程师视频教程
  17. ElacticSearch索引,文档,记录,常用指令
  18. oracle 取系统当前年份_Oracle 之 获取当前日期及日期格式化
  19. IDA7.7PJ版本
  20. O2OA开源免费办公平台:在PAAS平台上部署O2OA开发平台

热门文章

  1. 2022-2028年中国加密货币行业市场研究及前瞻分析报告
  2. 【VS实践】如何在vs中自动添加注释
  3. SpringBoot (二) :全局异常处理设置
  4. trinosql_prestosql问题
  5. LeetCode简单题之两个列表的最小索引总和
  6. 摄像头和相机模型和内参原理
  7. 2021年大数据Hadoop(七):HDFS分布式文件系统简介
  8. JAVA实现ftp服务端_用 java 实现FTP SERVER(附源码)
  9. git remote 命令
  10. 一个http的Post请求问题,unable to resolve host 我的域名:no address associated with hostnam...