作者:谢昆

一段伪代码反应整个touch事件的分发

  1. public boolean dispatchTouchEvent(MotionEvent event) {
  2. boolean consume = false;
  3. if (onInteceptTouchEvent(event)) {
  4. consume = onTouchEvent(event);
  5. } else {
  6. consume = child.dispatchTouchEvent(event);
  7. }
  8. return consume;
  9. }

View的事件分发

  1. public boolean dispatchTouchEvent(MotionEvent event) {
  2. if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
  3. mOnTouchListener.onTouch(this, event)) {
  4. return true;
  5. }
  6. return onTouchEvent(event);
  7. }
  1. 第一个参数是我们设置的onTouchListener
  2. 第二个参数(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的控件是否是enable的,按钮默认都是enable的,因此这个条件恒定为true
  3. 第三个参数就是mOnTouchListener.onTouch(this, event)方法的返回值

onTouchEvent(MotionEvent event)的源码:

  1. public boolean onTouchEvent(MotionEvent event) {
  2. final int viewFlags = mViewFlags;
  3. if ((viewFlags & ENABLED_MASK) == DISABLED) {
  4. // A disabled view that is clickable still consumes the touch
  5. // events, it just doesn't respond to them.
  6. return (((viewFlags & CLICKABLE) == CLICKABLE ||
  7. (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
  8. }
  9. if (mTouchDelegate != null) {
  10. if (mTouchDelegate.onTouchEvent(event)) {
  11. return true;
  12. }
  13. }
  14. if (((viewFlags & CLICKABLE) == CLICKABLE ||
  15. (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
  16. switch (event.getAction()) {
  17. case MotionEvent.ACTION_UP:
  18. boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
  19. if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
  20. // take focus if we don't have it already and we should in
  21. // touch mode.
  22. boolean focusTaken = false;
  23. if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
  24. focusTaken = requestFocus();
  25. }
  26. if (!mHasPerformedLongPress) {
  27. // This is a tap, so remove the longpress check
  28. removeLongPressCallback();
  29. // Only perform take click actions if we were in the pressed state
  30. if (!focusTaken) {
  31. // Use a Runnable and post this rather than calling
  32. // performClick directly. This lets other visual state
  33. // of the view update before click actions start.
  34. if (mPerformClick == null) {
  35. mPerformClick = new PerformClick();
  36. }
  37. if (!post(mPerformClick)) {
  38. performClick();
  39. }
  40. }
  41. }
  42. if (mUnsetPressedState == null) {
  43. mUnsetPressedState = new UnsetPressedState();
  44. }
  45. if (prepressed) {
  46. mPrivateFlags |= PRESSED;
  47. refreshDrawableState();
  48. postDelayed(mUnsetPressedState,
  49. ViewConfiguration.getPressedStateDuration());
  50. } else if (!post(mUnsetPressedState)) {
  51. // If the post failed, unpress right now
  52. mUnsetPressedState.run();
  53. }
  54. removeTapCallback();
  55. }
  56. break;
  57. case MotionEvent.ACTION_DOWN:
  58. if (mPendingCheckForTap == null) {
  59. mPendingCheckForTap = new CheckForTap();
  60. }
  61. mPrivateFlags |= PREPRESSED;
  62. mHasPerformedLongPress = false;
  63. postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
  64. break;
  65. case MotionEvent.ACTION_CANCEL:
  66. mPrivateFlags &= ~PRESSED;
  67. refreshDrawableState();
  68. removeTapCallback();
  69. break;
  70. case MotionEvent.ACTION_MOVE:
  71. final int x = (int) event.getX();
  72. final int y = (int) event.getY();
  73. // Be lenient about moving outside of buttons
  74. int slop = mTouchSlop;
  75. if ((x < 0 - slop) || (x >= getWidth() + slop) ||
  76. (y < 0 - slop) || (y >= getHeight() + slop)) {
  77. // Outside button
  78. removeTapCallback();
  79. if ((mPrivateFlags & PRESSED) != 0) {
  80. // Remove any future long press/tap checks
  81. removeLongPressCallback();
  82. // Need to switch from pressed to not pressed
  83. mPrivateFlags &= ~PRESSED;
  84. refreshDrawableState();
  85. }
  86. }
  87. break;
  88. }
  89. return true;
  90. }
  91. return false;
  92. }

首先在第14行我们可以看出,如果该控件是可以点击的就会进入到第16行的switch判断中去,而如果当前的事件是抬起手指,则会进入到MotionEvent.ACTION_UP这个case当中。在经过种种判断之后,会执行到第38行的performClick()方法

performClick()的源码:

  1. public boolean performClick() {
  2. sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
  3. if (mOnClickListener != null) {
  4. playSoundEffect(SoundEffectConstants.CLICK);
  5. mOnClickListener.onClick(this);
  6. return true;
  7. }
  8. return false;
  9. }

setOnClickListener(OnClickListener l)的源码:

  1. public void setOnClickListener(OnClickListener l) {
  2. if (!isClickable()) {
  3. setClickable(true);
  4. }
  5. mOnClickListener = l;
  6. }

ViewGroup的事件分发

  1. public boolean dispatchTouchEvent(MotionEvent ev) {
  2. final int action = ev.getAction();
  3. final float xf = ev.getX();
  4. final float yf = ev.getY();
  5. final float scrolledXFloat = xf + mScrollX;
  6. final float scrolledYFloat = yf + mScrollY;
  7. final Rect frame = mTempRect;
  8. boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
  9. if (action == MotionEvent.ACTION_DOWN) {
  10. if (mMotionTarget != null) {
  11. mMotionTarget = null;
  12. }
  13. if (disallowIntercept || !onInterceptTouchEvent(ev)) {
  14. ev.setAction(MotionEvent.ACTION_DOWN);
  15. final int scrolledXInt = (int) scrolledXFloat;
  16. final int scrolledYInt = (int) scrolledYFloat;
  17. final View[] children = mChildren;
  18. final int count = mChildrenCount;
  19. for (int i = count - 1; i >= 0; i--) {
  20. final View child = children[i];
  21. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
  22. || child.getAnimation() != null) {
  23. child.getHitRect(frame);
  24. if (frame.contains(scrolledXInt, scrolledYInt)) {
  25. final float xc = scrolledXFloat - child.mLeft;
  26. final float yc = scrolledYFloat - child.mTop;
  27. ev.setLocation(xc, yc);
  28. child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  29. if (child.dispatchTouchEvent(ev)) {
  30. mMotionTarget = child;
  31. return true;
  32. }
  33. }
  34. }
  35. }
  36. }
  37. }
  38. boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
  39. (action == MotionEvent.ACTION_CANCEL);
  40. if (isUpOrCancel) {
  41. mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
  42. }
  43. final View target = mMotionTarget;
  44. if (target == null) {
  45. ev.setLocation(xf, yf);
  46. if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
  47. ev.setAction(MotionEvent.ACTION_CANCEL);
  48. mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  49. }
  50. return super.dispatchTouchEvent(ev);
  51. }
  52. if (!disallowIntercept && onInterceptTouchEvent(ev)) {
  53. final float xc = scrolledXFloat - (float) target.mLeft;
  54. final float yc = scrolledYFloat - (float) target.mTop;
  55. mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  56. ev.setAction(MotionEvent.ACTION_CANCEL);
  57. ev.setLocation(xc, yc);
  58. if (!target.dispatchTouchEvent(ev)) {
  59. }
  60. mMotionTarget = null;
  61. return true;
  62. }
  63. if (isUpOrCancel) {
  64. mMotionTarget = null;
  65. }
  66. final float xc = scrolledXFloat - (float) target.mLeft;
  67. final float yc = scrolledYFloat - (float) target.mTop;
  68. ev.setLocation(xc, yc);
  69. if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
  70. ev.setAction(MotionEvent.ACTION_CANCEL);
  71. target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  72. mMotionTarget = null;
  73. }
  74. return target.dispatchTouchEvent(ev);
  75. }

在第13行可以看到一个条件判断,如果disallowIntercept和!onInterceptTouchEvent(ev)两者有一个为true,就会进入到这个条件判断中。接着如果进入条件语句就回去遍历查找点击的是哪一个子view,进而调用子view的dispatchTouchEvent(ev)方法将事件传递下去。

那如果遍历没有找到目标的子view,则会继续往下面走,会在第50行调用super.dispatchTouchEvent(ev),这里就是View中的dispatchTouchEvent(ev),这样就执行完整个的流程。

activity对事件的分发过程

activity的dispatchTouchEvent(MotionEvent ev)方法:

  1. public boolean dispatchTouchEvent(MotionEvent ev) {
  2. if (ev.getAction() == MotionEvent.ACTION_DOWN) {
  3. onUserInteraction();
  4. }
  5. if (getWindow().superDispatchTouchEvent(ev)) {
  6. return true;
  7. }
  8. return onTouchEvent(ev);
  9. }

主要就通过window来分发事件,如果没有view消费掉事件,就调用activity的onTouchEvent(ev)

Window是一个抽象类,他的实现类就是PhoneWindow,PhoneWindow中的superDispatchTouchEvent(ev)的方法:

  1. public boolean superDispatchTouchEvent(MotionEvent ev){
  2. return mDecor.superDispatchTouchEvent(ev);
  3. }

touch事件的分发机制的更多相关文章

  1. View,ViewGroup的Touch事件的分发机制

    原帖地址:http://blog.csdn.net/xiaanming/article/details/21696315 ViewGroup的事件分发机制 我们用手指去触摸Android手机屏幕,就会 ...

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

    转自:xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/21696315) 今天这篇文章主要分析的是Android的事件分发机制, ...

  3. Android事件分发机制(一) Touch 事件的分发和消费机制

    Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...

  4. Android 编程下 Touch 事件的分发和消费机制

    Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...

  5. Touch 事件的分发和消费机制

    Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...

  6. Android 编程下Touch 事件的分发和消费机制

    1.事件分发:public boolean dispatchTouchEvent(MotionEvent ev) Touch 事件发生时 Activity 的 dispatchTouchEvent(M ...

  7. 事件之Touch 事件的分发和消费机制

    Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...

  8. touch事件的分发和消费机制

    Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...

  9. Android 编程下Touch 事件的分发和消费机制和OnTouchListener,OnClickListener和OnLongClickListener的关系

    1.事件分发:public boolean dispatchTouchEvent(MotionEvent ev) Touch 事件发生时 Activity 的 dispatchTouchEvent(M ...

随机推荐

  1. Vue指令的概念

    指令(Directives) 是带有v- 前缀的特殊属性,指令属性是单一的js表达式. 指令的职责就是表达式的值发生变化时,在DOM中做出相应的回应. 如下例子: 实例 <div id=&quo ...

  2. PS切图基本操作

    PS切图基本操作 2016-05-11 20:56:46|  分类: PhotoShop|字号 订阅     下载LOFTER我的照片书  |     1首先在“文件”中打开一张图片.   2点击“移 ...

  3. 暴力搜索+散列--P1008 三连击

    题目描述 将1,2, ⋯,9共9个数分成3组,分别组成3个三位数,且使这3个三位数构成1:2:3的比例,试求出所有满足条件的3个三位数. 输入输出格式 输入格式: 木有输入 输出格式: 若干行,每行3 ...

  4. ZOJ - 3983 - Crusaders Quest(思维 + 暴力)

    题意: 给出一个字符串,长度为9,包含三种各三个字母"a","g","o",如果一次消除连续三个一样的分数+1,消完自动向左补齐 其中可以消 ...

  5. c++运行程序闪退

        以最简单程序为例       法一:在主函数末尾下一行getchar();即可.需要注意的是这种方法并不适合所有程序,       法二:<1>先在程序开头加上头文件#includ ...

  6. Linux之 sed用法

    sed是一个很好的文件处理工具,本身是一个管道命令,主要是以行为单位进行处理,可以将数据行进行替换.删除.新增.选取等特定工作,下面先了解一下sed的用法sed命令行格式为:         sed ...

  7. LINUX-用户和群组

    groupadd group_name 创建一个新用户组 groupdel group_name 删除一个用户组 groupmod -n new_group_name old_group_name 重 ...

  8. 【Codeforces 490C】Hacking Cypher

    [链接] 我是链接,点我呀:) [题意] 让你把一个字符串分成左右两个部分 形成两个正数 使得这两个正数一个能被a整除,一个能被b整除 找到任意一个解就可以 [题解] 枚举分割的断点i 枚举的时候用同 ...

  9. Docker学习总结(18)——阿里超大规模Docker化之路

    12月6-7日,由阿里巴巴集团.阿里巴巴技术发展部.阿里云云栖社区联合主办,以"2016双11技术创新"为主题的阿里巴巴技术论坛上,阿里巴巴研究员林昊分享了阿里超大规模Docker ...

  10. Leetcode 135.分糖果

    分发糖果 老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分. 你需要按照以下要求,帮助老师给这些孩子分发糖果: 每个孩子至少分配到 1 个糖果. 相邻的孩 ...