作者:谢昆

一段伪代码反应整个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. MySql(五)select排序查询

    举个栗子/**查询员工信息,要求工资按照从高到低进行排序(默认升序)**/SELECT * FROM employees ORDER BY salary ASC;/**方法2:**/SELECT * ...

  2. SDK_组合框的使用

    组合框的使用 组合框的创建:有三种风格,分别 Simaple,Dropdown(可输入), 下拉列表(不可输入) 可以通过可视化编程中下拉列表的 下拉箭头 设置列表的长度 如何向组合框控件中添加数据, ...

  3. JS中的let和var的区别

    最近很多前端的朋友去面试被问到let和var的区别,其实阮一峰老师的ES6中已经很详细介绍了let的用法和var的区别.我简单总结一下,以便各位以后面试中使用. ES6 新增了let命令,用来声明局部 ...

  4. SSH命令行传输文件到远程服务器

    Ubuntu操作系统 SCP命令 使用方式如下: 1.上传本地文件到远程服务器 scp /var/www/test.php root@192.168.0.101:/var/www/ 把本机/var/w ...

  5. HTML5网页如何调用浏览器APP的微信分享功能?

    if (/AppleWebKit.*Mobile/i.test(navigator.userAgent) || (/MIDP|SymbianOS|NOKIA|SAMSUNG|LG|NEC|TCL|Al ...

  6. HDU - 6264 - Super-palindrome(思维)

    题意: 给出一个字符串,使得所有的奇数连续子串为回文串,输出最小变化次数 思路: 分析过后,只存在两种情况,1全部为一种字母,2形如abab交替类型 对于奇数位和偶数位单独计数,只需计算出奇数位或者偶 ...

  7. UVA - 1620 Lazy Susan(逆序数)

    题目: 把1~n(n≤500)放到一个圆盘里,每个数恰好出现一次.每次可以选4个连续的数字翻转顺序.问能不能变成1.2.3....n的顺序. 思路: 这样的题的规律真的是一点都不好推,看了网上的博客知 ...

  8. Linux学习笔记记录(四)

  9. 带你全面分析嵌入式linux系统启动过程中uboot的作用

    资料链接:http://mp.weixin.qq.com/s/rYVchD-xy7Bdkc1O3fW2Wg

  10. 10.3andXE7的DEVExpress18.2.1记录备查

    记录备查: win10 DEVExpress18.2.1用DevExpressVCL一键编译安装工具_v10.3.2 - 2018-12-12.exe(包括help,备份...升级系统不用重新安装控件 ...