Android开发总结笔记 View的事件分发机制 3-9

来源:互联网 发布:手机网络有问号 编辑:程序博客网 时间:2024/05/27 00:41

参考 http://blog.csdn.net/yanbober/article/details/45887547


关于View事件分发机制,一般就是从两个方法开始。

  • setOnClickListener   回调onClick方法

  • setOnTouchListener   回调onTouch方法,其中有ACTION_DOWNACTION_UPACTION_MOVE方法



如果一个组件同时注册了两个事件,onTouch是先于onClick执行的

  1. mIvEditBarEdit.setOnTouchListener(new View.OnTouchListener() {
  2.            @Override
  3.            public boolean onTouch(View v, MotionEvent event) {
  4.                return false;
  5.            }
  6.        });

但是,有个关键点onTouch方法中有一个布尔类型的返回值,如果把这个返回值改成true

onClick方法就不会执行了,因为返回了true,所以事件被onTouch方法消费了,从而导致事件不再向下传递


从源码里面找理由(API 22)

首先要知道的一点是,只有触碰了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法

而这个方法在View类中

  1. public boolean dispatchTouchEvent(MotionEvent event) {
  2. // If the event should be handled by accessibility focus first.
  3. if (event.isTargetAccessibilityFocus()) {
  4. // We don't have focus or no virtual descendant has it, do not handle the event.
  5. if (!isAccessibilityFocusedViewOrHost()) {
  6. return false;
  7. }
  8. // We have focus and got the event, then use normal event dispatch.
  9. event.setTargetAccessibilityFocus(false);
  10. }
  11. boolean result = false;
  12. if (mInputEventConsistencyVerifier != null) {
  13. mInputEventConsistencyVerifier.onTouchEvent(event, 0);
  14. }
  15. final int actionMasked = event.getActionMasked();
  16. if (actionMasked == MotionEvent.ACTION_DOWN) {
  17. // Defensive cleanup for new gesture
  18. stopNestedScroll();
  19. }
  20. if (onFilterTouchEventForSecurity(event)) {
  21. //noinspection SimplifiableIfStatement
  22. ListenerInfo li = mListenerInfo;
  23. if (li != null && li.mOnTouchListener != null
  24. && (mViewFlags & ENABLED_MASK) == ENABLED
  25. && li.mOnTouchListener.onTouch(this, event)) {
  26. result = true;
  27. }
  28. if (!result && onTouchEvent(event)) {
  29. result = true;
  30. }
  31. }
  32. if (!result && mInputEventConsistencyVerifier != null) {
  33. mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
  34. }
  35. // Clean up after nested scrolls if this is the end of a gesture;
  36. // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
  37. // of the gesture.
  38. if (actionMasked == MotionEvent.ACTION_UP ||
  39. actionMasked == MotionEvent.ACTION_CANCEL ||
  40. (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
  41. stopNestedScroll();
  42. }
  43. return result;
  44. }

前面都是一些标记的设置和手势的传递,看20

onFilterTouchEventForSecurity(event)判断组件有没有被遮住

ListenerInfo是View的静态内部类,用来定义各种XXXListener方法

重点是 23

  • li!=null

  • li.mTouchListener!=null   只要调用了setonTouchListener方法,这个值就不为空

  • (mViewFlags & ENABLED_MASK) == ENABLED    只要View是可点击的,就为真

  • li.mOnTouchListener.onTouch(this, event)               而这个值,是最关键的,是onTouch方法的返回值


如果这四个条件都成立,接下来if (!result && onTouchEvent(event))就不会执行,最终dispatchTouchEvent就会返回true

如果有一个条件不成立的话,if (!result && onTouchEvent(event))就会执行,onTouchEvent(event))返回falsedispatchTouchEvent返回false

那么这样的话,点击事件就一定在onTouchEvent(event)方法中

  1. public boolean onTouchEvent(MotionEvent event) {
  2. final float x = event.getX();
  3. final float y = event.getY();
  4. final int viewFlags = mViewFlags;
  5. final int action = event.getAction();
  6. if ((viewFlags & ENABLED_MASK) == DISABLED) {
  7. if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
  8. setPressed(false);
  9. }
  10. // A disabled view that is clickable still consumes the touch
  11. // events, it just doesn't respond to them.
  12. return (((viewFlags & CLICKABLE) == CLICKABLE
  13. || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
  14. || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
  15. }
  16. if (mTouchDelegate != null) {
  17. if (mTouchDelegate.onTouchEvent(event)) {
  18. return true;
  19. }
  20. }
  21. if (((viewFlags & CLICKABLE) == CLICKABLE ||
  22. (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
  23. (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
  24. switch (action) {
  25. case MotionEvent.ACTION_UP:
  26. boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
  27. if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
  28. // take focus if we don't have it already and we should in
  29. // touch mode.
  30. boolean focusTaken = false;
  31. if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
  32. focusTaken = requestFocus();
  33. }
  34. if (prepressed) {
  35. // The button is being released before we actually
  36. // showed it as pressed. Make it show the pressed
  37. // state now (before scheduling the click) to ensure
  38. // the user sees it.
  39. setPressed(true, x, y);
  40. }
  41. if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
  42. // This is a tap, so remove the longpress check
  43. removeLongPressCallback();
  44. // Only perform take click actions if we were in the pressed state
  45. if (!focusTaken) {
  46. // Use a Runnable and post this rather than calling
  47. // performClick directly. This lets other visual state
  48. // of the view update before click actions start.
  49. if (mPerformClick == null) {
  50. mPerformClick = new PerformClick();
  51. }
  52. if (!post(mPerformClick)) {
  53. performClick();
  54. }
  55. }
  56. }
  57. if (mUnsetPressedState == null) {
  58. mUnsetPressedState = new UnsetPressedState();
  59. }
  60. if (prepressed) {
  61. postDelayed(mUnsetPressedState,
  62. ViewConfiguration.getPressedStateDuration());
  63. } else if (!post(mUnsetPressedState)) {
  64. // If the post failed, unpress right now
  65. mUnsetPressedState.run();
  66. }
  67. removeTapCallback();
  68. }
  69. mIgnoreNextUpEvent = false;
  70. break;
  71. case MotionEvent.ACTION_DOWN:
  72. mHasPerformedLongPress = false;
  73. if (performButtonActionOnTouchDown(event)) {
  74. break;
  75. }
  76. // Walk up the hierarchy to determine if we're inside a scrolling container.
  77. boolean isInScrollingContainer = isInScrollingContainer();
  78. // For views inside a scrolling container, delay the pressed feedback for
  79. // a short period in case this is a scroll.
  80. if (isInScrollingContainer) {
  81. mPrivateFlags |= PFLAG_PREPRESSED;
  82. if (mPendingCheckForTap == null) {
  83. mPendingCheckForTap = new CheckForTap();
  84. }
  85. mPendingCheckForTap.x = event.getX();
  86. mPendingCheckForTap.y = event.getY();
  87. postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
  88. } else {
  89. // Not inside a scrolling container, so show the feedback right away
  90. setPressed(true, x, y);
  91. checkForLongClick(0);
  92. }
  93. break;
  94. case MotionEvent.ACTION_CANCEL:
  95. setPressed(false);
  96. removeTapCallback();
  97. removeLongPressCallback();
  98. mInContextButtonPress = false;
  99. mHasPerformedLongPress = false;
  100. mIgnoreNextUpEvent = false;
  101. break;
  102. case MotionEvent.ACTION_MOVE:
  103. drawableHotspotChanged(x, y);
  104. // Be lenient about moving outside of buttons
  105. if (!pointInView(x, y, mTouchSlop)) {
  106. // Outside button
  107. removeTapCallback();
  108. if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
  109. // Remove any future long press/tap checks
  110. removeLongPressCallback();
  111. setPressed(false);
  112. }
  113. }
  114. break;
  115. }
  116. return true;
  117. }
  118. return false;
  119. }

从这段源码的第24行看起,如果当前组件可以点击,就会进入到swich,然后就在case ACTION_UP中经过一系列的判断后调用了performClick方法


关于touch事件的一系列ACTION_UP,ACTION_DOWN和ACTION_MOVE方法

在上面的onTouchEvent方法中可以看到,只要组件可以点击,走到 24行的里面的判断,不管ACTION是什么,最终都会返回134行的true

所以,如果想要拦截ACTION事件,把组件设为不可点击即可

那样的话,onTouchEvent方法就不会进入24行的判断而直接返回137false,只有ACTION_DOWN得到执行


所以,只有前一个action返回true,才会触发下一个action

0 0