onTouch,onClick冲突详解
来源:互联网 发布:现场网络直播 编辑:程序博客网 时间:2024/06/18 11:41
onTouch,onClick冲突详解
ONE Goal,ONE Passion!
概述:
ViewGroup和View的分发.
http://blog.csdn.net/fengltxx/article/details/49330343
介绍一个OnTouchEvent中事件流程
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downX = (int) event.getX(); System.out.println("---ACTION_DOWN--"); break; case MotionEvent.ACTION_MOVE: System.out.println("---ACTION_MOVE--"); break; case MotionEvent.ACTION_UP: upX = (int) event.getX(); System.out.println("---ACTION_UP--"); if (Math.abs(downX - upX) > 30) { return true; } break; } return super.onTouchEvent(event); }
咦…….为什么要返回一个super.onTouchEvent(event).
注意:系统想接收到此事件.
- 1.return super.onTouchEvent(event);
- 2.调用super.onTouchEvent(event)即:(将这句代码放在第onTouchEvent(MotionEvent event)方法);
开始我们的测试
在ACTION_DOWN中return true.
case MotionEvent.ACTION_DOWN: downX = (int) event.getX(); System.out.println("---ACTION_DOWN--"); return true;
当我们点击(down)view并且抬起(up)时.onclick就不能被回调.为什么呢?因为我们没有将down事件传递给系统.仅仅把up事件传递了. 为什么不传递down事件就不能执行onclick呢?going..
来看看super.onTouchEvent(event)到底做了什么?
public boolean onTouchEvent(MotionEvent event) { final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); } if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } } if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0; if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { // take focus if we don't have it already and we should in // touch mode. boolean focusTaken = false; if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); } if (prepressed) { // The button is being released before we actually // showed it as pressed. Make it show the pressed // state now (before scheduling the click) to ensure // the user sees it. setPressed(true); } if (!mHasPerformedLongPress) { // This is a tap, so remove the longpress check removeLongPressCallback(); // Only perform take click actions if we were in the pressed state if (!focusTaken) { // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start. if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { performClick(); } } } if (mUnsetPressedState == null) { mUnsetPressedState = new UnsetPressedState(); } if (prepressed) { postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break; case MotionEvent.ACTION_DOWN: mHasPerformedLongPress = false; if (performButtonActionOnTouchDown(event)) { break; } // Walk up the hierarchy to determine if we're inside a scrolling container. boolean isInScrollingContainer = isInScrollingContainer(); // For views inside a scrolling container, delay the pressed feedback for // a short period in case this is a scroll. if (isInScrollingContainer) { mPrivateFlags |= PFLAG_PREPRESSED; if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { // Not inside a scrolling container, so show the feedback right away setPressed(true); checkForLongClick(0); } break; case MotionEvent.ACTION_CANCEL: setPressed(false); removeTapCallback(); removeLongPressCallback(); break; case MotionEvent.ACTION_MOVE: final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons if (!pointInView(x, y, mTouchSlop)) { // Outside button removeTapCallback(); if ((mPrivateFlags & PFLAG_PRESSED) != 0) { // Remove any future long press/tap checks removeLongPressCallback(); setPressed(false); } } break; } return true; } return false; }
我们先找到onClick在哪个地方被回调.嘎嘎 …找到了在UP事件中: performClick();
public boolean performClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); // 是不是很熟悉.这里就是onClick的回调 return true; } return false; }
哎呦!想要执行performClick();方法好像要经过2层判断.首先我们先看第一层判断.
第一层判断
找到第28行.
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed)
系统判断 mPrivateFlags & PFLAG_PRESSED 和 prepressed的值是否有一个为true.这两个值和什么有关呢?
看DOWN中的第90行:
mPrivateFlags |= PFLAG_PREPRESSED;
做完这个处理第28行判断就为true了.
噢.原来没有DOWN事件.根本没办法执行UP中performClick()中代码.所以我们的onClick事件不能被回调.
如果我们在自定义的View中DOWN中返回Super.onTouchEvent()就可以接收onClick事件了吗?.NO !
别忘了我们还有第二层判断:
找到第44行
if (!mHasPerformedLongPress) //判断是否已经执行长按
哎呦喂!这个mHasPerformedLongPress是什么鬼? 想一下:如果这个值为false那么不就可以突破第二层判断了嘛!答案是–YES. 可是会不会在什么时候系统将mHasPerformedLongPress设置为True呢? 所以我们一路Ctrl+F.(mHasPerformedLongPress = true;)下一步.终于找到了.
class CheckForLongPress implements Runnable { private int mOriginalWindowAttachCount; public void run() { if (isPressed() && (mParent != null) && mOriginalWindowAttachCount == mWindowAttachCount) { if (performLongClick()) { mHasPerformedLongPress = true; } } }
系统在CheckForLongPress代码块中将mHasPerformedLongPress = true.谁执行了CheckForLongPress呢?
继续找:
private void checkForLongClick(int delayOffset) { if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { mHasPerformedLongPress = false; if (mPendingCheckForLongPress == null) { mPendingCheckForLongPress = new CheckForLongPress(); } mPendingCheckForLongPress.rememberWindowAttachCount(); postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout() - delayOffset); } }
bingo!checkForLongClick方法中执行了CheckForLongPress.又是谁执行了checkForLongClick呢?可以看到有很多地方都执行了这个方法.我们找到在CheckForTap方法中执行了checkForLongClick.
//检查是否是点击事件 private final class CheckForTap implements Runnable { public void run() { mPrivateFlags &= ~PFLAG_PREPRESSED; setPressed(true); //在点击事件中检查是否是长按事件 checkForLongClick(ViewConfiguration.getTapTimeout()); } }
继续找CheckForTap.看看到底在哪个地方执行.哇哦!
找到第91-94.在DOWN中执行有这段代码:
if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); // 延迟 180ms后执行mPendingCheckForTap代码块.目的就是检查事件是点击DOWN还是长按事件 postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
最后我们看到.如果代码执行到了CheckForLongPress方法中在将 mHasPerformedLongPress = true;之前还有一个判断if (performLongClick()).只有当performLongClick() = true时.才会将mHasPerformedLongPress赋值为true.
等一下: performLongClick()又是什么鬼从字面意思可以看出.就是执行长点击.我们可以重写这个方法.通过return true还是false来控制mHasPerformedLongPress是否可以被赋值为true.
我去…..终于搞完了.
总结下来就是:
- 1.当手指点下,在onTouchEvent中如果调用了super.onTouchEvent将事件传递给了系统.系统会先执行Action_DOWN.
- 2.在DOWN中,首先将mHasPerformedLongPress = false.然后调用postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());去检查是否是敲击事件.
- 3.在mPendingCheckForTap代码块中会去延时调用checkForLongClick().去检查是否是长点击事件.
- 4.在checkForLongClick()中再去延时调用CheckForLongPress(),去检查是否是长按事件
- 5.在CheckForLongPress()中会根据performLongClick()的返回值来决定是否将mHasPerformedLongPress = true.
每次检查都是延时操作.如果在还没有检查完,就抬起的话.由于在down是已经将mHasPerformedLongPress= false.所以onclick事件是可以执行回调的.
可以看出:
- 1.长按事件是在DOWN事件中判断的.
- 2.点击事件也要经过DOWN事件,而且和长按事件返回值有关.如果返回true(可以理解为将事件消费了)—执行长按就不能执行onclick.返回false同样可以执行onClick
注: ViewGroup和View的分发机制详解.
http://blog.csdn.net/fengltxx/article/details/49330343
- onTouch,onClick冲突详解
- android onTouch与onClick冲突解决方法
- 悬浮窗onTouch和onCLick的冲突
- android ontouch和onclick冲突处理
- android的onClick()和onTouch()方法详解
- 如何解决ViewFlipper的onClick和onTouch的冲突事件
- Android关于OnTouch 和OnClick同时调用冲突的解决方案
- Android 中 onTouch 和OnClick 冲突的处理
- Android关于OnTouch 和OnClick同时调用冲突的解决方案
- Android关于OnTouch 和OnClick同时调用冲突的解决方案
- Android 中 onTouch 和OnClick 冲突的处理
- Android关于OnTouch 和OnClick同时调用冲突的解决方案
- Android onTouch、OnLongClick、onClick及ScrollView滑动事件冲突
- Android关于OnTouch 和OnClick同时调用冲突 重复
- Android onTouch、OnLongClick、onClick及ScrollView滑动事件冲突
- Android onTouch、OnLongClick、onClick及ScrollView滑动事件冲突
- 关于OnTouch 和OnClick同时调用冲突的解决方案
- Android关于OnTouch 和OnClick同时调用冲突的解决方案
- 机器学习基础
- BZOJ2209 [Jsoi2011]括号序列
- 面向对象与形而上学
- 使用Spring MVC的@ControllerAdvice注解做Json的异常处理
- android学习总结(16.08.23)LinearLayout(线性布局)中控件的摆放位置——orientation,gravity,layout_gravity的关系
- onTouch,onClick冲突详解
- stm32掉电前的数据存储到flash
- 基于Redis实现分布式锁
- Batch Normalization 总结
- 二叉树的存储结构
- Linux tomcat8,JDK8 启动卡住半天 后才能正常启动解决方案
- mysl lock table read
- 大牛解密阿里云直播技术平台
- ubuntu出现您的当前网络有.local域