Android 事件分发机制View篇
来源:互联网 发布:阿迪达斯网络授权书 编辑:程序博客网 时间:2024/05/29 00:35
这两天研究了一下Android中的事件分发机制,在这里和大家分享下学习成果。首先我们先看一个简单的例子:
1.button.setOnClickListener(new OnClickListener() { 2. @Override 3. public void onClick(View v) { 4. Log.d("TAG", "onClick execute"); 5. } 6.});很显然这是一个button被设置了点击事件,只要一点击这个button就会触发onClick方法,就会打印这log。可是如果我们同时给button加上一个touch事件的监听,重写一下ontouch方法,那么结果又会如何呢。
1.button.setOnTouchListener(new OnTouchListener() { 2. @Override 3. public boolean onTouch(View v, MotionEvent event) { 4. Log.d("TAG", "onTouch execute, action " + event.getAction()); 5. return false; 6. } 7.});大家想想一下,是onclick先执行还是ontouch先执行?下面让我们看下结果。
可以看到是ontouch先执行,而且是执行两次的。
那么我们先得到一个简单的结论:ontouch是先于onclick执行的。
下面让我们改下代码,把ontouch中的返回值改成true,那么结果会怎样呢?
可以看到,onclick方法没有执行,这是怎么回事呢,让我们简单拔一下源码,便知分晓。
首先我们知道,一个当我们触摸到了一个控件一定会先调用他的dispatchTouchEvent方法,那么我们就从这个方法看起。我们进入button的源码发现没有找到dispatchTouchEvent这个方法,找寻他的父类textview依然没有,最终在他的爷爷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); }我们看这个方法很简单,首先看if中的条件,在if中有三个条件,第一个条件在view这个类中可以找到setOnTouchListener这个方法对mOnTouchListener进行了赋值,所以第一个条件一定是成立的;那么我们看第二个条件,这是判断当前控件是不是可用的,这个很显然也成立;那么就看第三个条件了,第三个条件是判断的ontouch方法的返回值,第一次我们没有修改他的返回值,返回的是false,所以导致if不成立。调用了onTouchEvent方法,然后onclick调用了,由此可以看出是onTouchEvent中调用了onclick方法。那么我们来看下第二次我们改变了ontouch方法中的返回值为true,这时if条件全部成立,返回了true,所以onTouchEvent方法没有执行,所以onclick方法也就没有执行。
如果我们把当前的button改成textview,再去为textview设置onclick方法和ontouch方法,这时结果又会怎样呢?
1.textView.setOnClickListener(new OnClickListener() { 2. @Override 3. public void onClick(View v) { 4. Log.d("TAG", "onClick execute"); 5. } 6.});
1.textView.setOnTouchListener(new OnTouchListener() { 2. @Override 3. public boolean onTouch(View v, MotionEvent event) { 4. Log.d("TAG", "onTouch execute, action " + event.getAction()); 5. return false; 6. } });
我们注意ontouch返回的是false,大家看下结果:
我们发现只打印了一条log,而且是只执行了一次ontouch方法,这是怎么回事呢?按照我们上面的分析ontouch方法返回false,dispatchTouchEvent方法中的if条件不成立,应该执行ontouchEvent方法,并会执行onclick方法,为何onclick方法没有执行呢?
让我们在看下ontouchEvent的源码,就可以得到答案。
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; }从第14行我们可以看出只要控件是可点击的,就会进入到第三个if中,而且当手指抬起是会调用一个叫performClick的方法,所以如果控件是button会执行performClick这个方法,而TextView是不会执行这个方法的因为TextView是不可点击的,所以textview会直接返回91行的false,一个触摸事件的完整流程是DOWN开始,UP结束,只有当中返回值一直是true才会从DOWN走到UP,如果当中出现false就会中断这个点击事件,因为TextView在ontouchEvent中返回了false,所以只会在触摸的时候响应一下,所以只打印出了一条日志。
差点忘了去执行performClick方法的button,我们看下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.}可以看到onclick方法在这里面执行了,button会将触摸事件响应到底,从DOWN到UP。
好了,我们说了这么多简单做下总结:把 touch 事件分发给 View 是通过调用 View 的 dispatchTouchEvent 方法来实现,View 的 dispatchTouchEvent 方法对事件进行了处理,主要做了两步:
- 第一步把事件给 View 的 mOnTouchListener 来处理,通过调用 mOnTouchListener.onTouch 方法来处理事件
- 第二步如果 mOnTouchListener 没有把事件消耗掉,就继续把事件给 View 的 onTouchEvent 方法来处理
View 的 onTouchEvent 方法的作用主要有两个,一个是作用把 touch 事件转换为 click 事件,产生 click 调用;另外一个作用是处理 view 按压状态的变化和焦点的分。touch 事件转换为 click 事件是在 motionEvent 为 up 类型的时候,调用了 performClick 方法,在 performClick 方法中执行 mOnClickListener 的 onclick 方法来执行点击事件。
- Android 事件分发机制View篇
- Android事件分发机制之View篇
- android事件分发机制之View篇
- Android事件分发机制(View篇)
- android View事件分发机制。
- Android View事件分发机制
- android view事件分发机制
- Android View 事件分发机制
- Android:View事件分发机制
- Android事件分发机制-------View
- Android View事件分发机制
- android事件分发机制view
- Android View 事件分发机制
- Android View事件分发机制
- Android View 事件分发机制
- Android View事件分发机制
- Android View事件分发机制
- Android View 事件分发机制源码详解(View篇)
- Python 中使用 string-escape 将带转义的字节码字符串转换为 utf-8 字符串
- HTML5 indexedDB数据库二之创建索引
- 带权有向图
- 【LEETCODE】26-Remove Duplicates from Sorted Array
- Android: JNI动态注册
- Android 事件分发机制View篇
- Windows10下JDK、eclipse与SDK及ADT的配置
- 关于TCP的滑动窗口和拥塞控制
- Android广播机制-BroadcastReceiver
- OCP课程之ORACLE审计
- 【jQuery】:visible过滤选择器
- Linux之hello驱动编写
- 用linux制作Mac OS U盘启动
- Appstore检查自动更新时间