View 事件处理
2016-03-25 10:47
405 查看
让我自己看源码是看不懂的,还好有这么多大神。
对于 View (非 ViewGroup)来说,dispatchTouchEvent 方法被理解为 "handleEvent" 可能会更会合适。
该方法的返回值表示 Event 是否被 View 消费了,true 表示消费了。
点击后日志如下:
03-25 10:48:00.296 18114-18114/com.unicorn.viewtry E/MyView: dispatchTouchEvent start
03-25 10:48:00.296 18114-18114/com.unicorn.viewtry E/MyView: dispatchTouchEvent end false
基本 View 并没有消费掉 Event。
03-25 11:46:56.121 31150-31150/com.unicorn.viewtry E/MyView: dispatchTouchEvent start
03-25 11:46:56.121 31150-31150/com.unicorn.viewtry E/MyView: onTouchListener onTouch start
03-25 11:46:56.121 31150-31150/com.unicorn.viewtry E/MyView: onTouchListener onTouch end false
03-25 11:46:56.121 31150-31150/com.unicorn.viewtry E/MyView: dispatchTouchEvent end false
如果
这个函数比较复杂,一点点看。
只要 View 是 clickable 或者 long_clickable 的。
点击日志如下:
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: dispatchTouchEvent start
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: onTouchListener onTouch start
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: onTouchListener onTouch end false
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: onTouchEvent start
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: onTouchEvent end false
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: dispatchTouchEvent end false
默认的 View 是不可点击的,所以事件还没被消费。
看 checkForLongClick 函数:
长按日志如下:
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: dispatchTouchEvent start
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: onTouchListener onTouch start
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: onTouchListener onTouch end false
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: onTouchEvent start
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: onTouchEvent end true
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: dispatchTouchEvent end true
03-25 12:35:54.700 25735-25735/com.unicorn.viewtry E/MyView: onLongClickListener onLongClick start
03-25 12:35:54.700 25735-25735/com.unicorn.viewtry E/MyView: onLongClickListener onLongClick end false
长按事件触发距离事件分发结束相差约 500 毫秒。
因为设置了 OnLongClickListener,View 成为 long_clickable 的,所以 onTouchEvent 消费了事件。
onLongClick 的返回值只和是否触发 OnClickListener 有关。
长按日志如下:
03-25 13:25:22.730 15131-15131/com.unicorn.viewtry E/MyView 0: dispatchTouchEvent start
03-25 13:25:22.730 15131-15131/com.unicorn.viewtry E/MyView 0: onTouchListener onTouch start
03-25 13:25:22.731 15131-15131/com.unicorn.viewtry E/MyView 0: onTouchListener onTouch end false
03-25 13:25:22.731 15131-15131/com.unicorn.viewtry E/MyView 0: onTouchEvent start
03-25 13:25:22.731 15131-15131/com.unicorn.viewtry E/MyView 0: onTouchEvent end true
03-25 13:25:22.731 15131-15131/com.unicorn.viewtry E/MyView 0: dispatchTouchEvent end true
03-25 13:25:23.230 15131-15131/com.unicorn.viewtry E/MyView: onLongClickListener onLongClick start
03-25 13:25:23.230 15131-15131/com.unicorn.viewtry E/MyView: onLongClickListener onLongClick end false
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: dispatchTouchEvent start
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: onTouchListener onTouch start
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: onTouchListener onTouch end false
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: onTouchEvent start
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: onTouchEvent end true
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: dispatchTouchEvent end true
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView: onClickListener onClick start
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView: onClickListener onClick end false
这里有个小细节,当 Down event(0 和 1 分别代表 Down 和 Up) 被 View 消费后。
View 才会收到对应的 Up event,这就涉及到 ViewGroup 是如何分发事件了。
推荐:
http://blog.csdn.net/dmk877/article/details/48781845
1. dispatchTouchEvent
ViewGroup 在分发事件时会调用子 View 的 dispatchTouchEvent 方法。对于 View (非 ViewGroup)来说,dispatchTouchEvent 方法被理解为 "handleEvent" 可能会更会合适。
该方法的返回值表示 Event 是否被 View 消费了,true 表示消费了。
public class MyView extends View { public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.e("MyView", "dispatchTouchEvent start" ); boolean result = super.dispatchTouchEvent(event); Log.e("MyView", "dispatchTouchEvent end " + result); return result; } }
点击后日志如下:
03-25 10:48:00.296 18114-18114/com.unicorn.viewtry E/MyView: dispatchTouchEvent start
03-25 10:48:00.296 18114-18114/com.unicorn.viewtry E/MyView: dispatchTouchEvent end false
基本 View 并没有消费掉 Event。
2. dispatchTouchEvent 源码
来看看 View 是如何 handle event 的,重点的部分可能就这么点,源码是 6.0 版本的。boolean result = false; ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; } return result;
3. OnTouchListener
添加 OnTouch 事件findViewById(R.id.myView).setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.e("MyView", "onTouchListener onTouch start"); Log.e("MyView", "onTouchListener onTouch end " + false); // 不消费事件 return false; } });点击后日志如下:
03-25 11:46:56.121 31150-31150/com.unicorn.viewtry E/MyView: dispatchTouchEvent start
03-25 11:46:56.121 31150-31150/com.unicorn.viewtry E/MyView: onTouchListener onTouch start
03-25 11:46:56.121 31150-31150/com.unicorn.viewtry E/MyView: onTouchListener onTouch end false
03-25 11:46:56.121 31150-31150/com.unicorn.viewtry E/MyView: dispatchTouchEvent end false
如果
findViewById(R.id.myView).setEnabled(false);那么 OnTouch 事件不会被触发。
4. onTouchEvent
如果 OnTouchListener 没消费掉事件,那么就轮到 onTouchEvent 了。这个函数比较复杂,一点点看。
if ((viewFlags & ENABLED_MASK) == DISABLED) { if (action == 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) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE); }注释上说,disabled 的 View 依旧会消费事件,即使它什么也没做。
只要 View 是 clickable 或者 long_clickable 的。
if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } }假如设置了 TouchDelegate ,那么由它来负责。
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { // 之后分析 return true; } return false;如果 View 是 clickable 或者 long_clickable 的,则消费事件。
点击日志如下:
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: dispatchTouchEvent start
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: onTouchListener onTouch start
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: onTouchListener onTouch end false
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: onTouchEvent start
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: onTouchEvent end false
03-25 12:24:17.964 20423-20423/com.unicorn.viewtry E/MyView: dispatchTouchEvent end false
默认的 View 是不可点击的,所以事件还没被消费。
5. OnLongClickListener
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(); } mPendingCheckForTap.x = event.getX(); mPendingCheckForTap.y = event.getY(); postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { // Not inside a scrolling container, so show the feedback right away setPressed(true, x, y); checkForLongClick(0); } break;
看 checkForLongClick 函数:
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); } }这里 post 了一个 500 毫秒的延时事件。
private final class CheckForLongPress implements Runnable { private int mOriginalWindowAttachCount; @Override public void run() { if (isPressed() && (mParent != null) && mOriginalWindowAttachCount == mWindowAttachCount) { if (performLongClick()) { mHasPerformedLongPress = true; } } } public void rememberWindowAttachCount() { mOriginalWindowAttachCount = mWindowAttachCount; } }如果 500 毫秒后 View 依旧是 pressed 状态,则执行 performLongClick 函数,并将返回结果保存在 mHasPerformedLongPress 中。
public boolean performLongClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); boolean handled = false; ListenerInfo li = mListenerInfo; if (li != null && li.mOnLongClickListener != null) { handled = li.mOnLongClickListener.onLongClick(View.this); } if (!handled) { handled = showContextMenu(); } if (handled) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); } return handled; }在 performLongClick 函数中 OnLongClick 事件被触发。
长按日志如下:
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: dispatchTouchEvent start
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: onTouchListener onTouch start
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: onTouchListener onTouch end false
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: onTouchEvent start
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: onTouchEvent end true
03-25 12:35:54.199 25735-25735/com.unicorn.viewtry E/MyView 0: dispatchTouchEvent end true
03-25 12:35:54.700 25735-25735/com.unicorn.viewtry E/MyView: onLongClickListener onLongClick start
03-25 12:35:54.700 25735-25735/com.unicorn.viewtry E/MyView: onLongClickListener onLongClick end false
长按事件触发距离事件分发结束相差约 500 毫秒。
因为设置了 OnLongClickListener,View 成为 long_clickable 的,所以 onTouchEvent 消费了事件。
onLongClick 的返回值只和是否触发 OnClickListener 有关。
6. OnClickListener
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, x, y); } if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { // 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(); } mIgnoreNextUpEvent = false; break;如果 OnLongClickListener 被执行且返回 true,那么 perfermClick 函数不会执行。
public boolean performClick() { final boolean result; final ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); result = true; } else { result = false; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); return result; }
长按日志如下:
03-25 13:25:22.730 15131-15131/com.unicorn.viewtry E/MyView 0: dispatchTouchEvent start
03-25 13:25:22.730 15131-15131/com.unicorn.viewtry E/MyView 0: onTouchListener onTouch start
03-25 13:25:22.731 15131-15131/com.unicorn.viewtry E/MyView 0: onTouchListener onTouch end false
03-25 13:25:22.731 15131-15131/com.unicorn.viewtry E/MyView 0: onTouchEvent start
03-25 13:25:22.731 15131-15131/com.unicorn.viewtry E/MyView 0: onTouchEvent end true
03-25 13:25:22.731 15131-15131/com.unicorn.viewtry E/MyView 0: dispatchTouchEvent end true
03-25 13:25:23.230 15131-15131/com.unicorn.viewtry E/MyView: onLongClickListener onLongClick start
03-25 13:25:23.230 15131-15131/com.unicorn.viewtry E/MyView: onLongClickListener onLongClick end false
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: dispatchTouchEvent start
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: onTouchListener onTouch start
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: onTouchListener onTouch end false
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: onTouchEvent start
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: onTouchEvent end true
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView 1: dispatchTouchEvent end true
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView: onClickListener onClick start
03-25 13:25:23.696 15131-15131/com.unicorn.viewtry E/MyView: onClickListener onClick end false
这里有个小细节,当 Down event(0 和 1 分别代表 Down 和 Up) 被 View 消费后。
View 才会收到对应的 Up event,这就涉及到 ViewGroup 是如何分发事件了。
推荐:
http://blog.csdn.net/dmk877/article/details/48781845
相关文章推荐
- No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?
- Oracle基础
- python常用函数(zip,map,filter,reduce)
- iOS开发基础 - UITableView
- php错误级别
- 使用 block 的小技巧和注意事项
- stl中string作为成员变量引起的core问题
- Restful架构学习
- 解析 iOS 动画原理与实现
- ADS简单程序编译分析(2)
- 策略模式(strategy)
- 外观模式
- 解决ssh访问linux虚拟机特别慢
- 匹配符
- 神经网络翻译系统dl4mt源码之Numpy库相关函数的使用(一)
- 字符串匹配的KMP算法
- iOS开发基础 - APP生命周期及启动页面
- java overriding
- CocoaPods详解之----使用篇
- Android系列--DOM、SAX、Pull解析XML