Android触摸事件总结
Android触摸事件总结如下:
1、触摸事件分发顺序
dispatchTouchEvent:触摸事件从上向下分发
onInterTouchEvent:拦截事件,返回true表示拦截成功,交由自己的onTouchEvent处理。
onTouchEvent:消费事件,如果消费返回true,否则返回false,事件再依次向上传递。
2、层级顺序
Activity--------layout中的根节点------ViewGroup-----当前targetView.
1.1 Activity中
dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { //交由view处理 return true; } return onTouchEvent(ev); //如果view不消费事件,则交给自己的onTouchEvent处理. }
onTouchEvent
消费事件
1.2 ViewGroup中
dispatchTouchEvent
ViewGroup重写了View中的dispatchTouchEvent
// Check for interception. final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; //targetView是否设置了requestDisallowInterceptTouchEvent(true) if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); //执行事件拦截 ev.setAction(action); // restore action in case it was changed } else { intercepted = false; //targetView不允许父容器拦截事件 } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true; }
onInterceptTouchEvent
拦截事件
onTouchEvent
消费事件,同View
1.3 子View中
dispatchTouchEvent
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { //如果当前View enabled,则优先执行onTouchListener,如果返回true,则不再执行onTouchEvent result = true; } if (!result && onTouchEvent(event)) { //执行onTouchEvent result = true; }
onTouchEvent
public boolean onTouchEvent(MotionEvent event) { final float x = event.getX(); final float y = event.getY(); final int viewFlags = mViewFlags; final int action = event.getAction();
//如果当前View.setEnabled(false); 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); } if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { //事件委托处理 return true; } } //可点击或长按可点击 if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { switch (action) { 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); } //如果还未执行长按事件(按下的时间不够或者没有设置LongClickListener 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; 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; case MotionEvent.ACTION_CANCEL: setPressed(false); removeTapCallback(); removeLongPressCallback(); mInContextButtonPress = false; mHasPerformedLongPress = false; mIgnoreNextUpEvent = false; break; case MotionEvent.ACTION_MOVE: drawableHotspotChanged(x, y); // Be lenient about moving outside of buttons if (!pointInView(x, y, mTouchSlop)) { //如果触摸点不在当前View上,则移除点击事件回调 // Outside button removeTapCallback(); if ((mPrivateFlags & PFLAG_PRESSED) != 0) { // Remove any future long press/tap checks removeLongPressCallback(); setPressed(false); } } break; } return true; } return false; }
dispatchTouchEvent、onTouchEvent、OnTouchListener、onClickListener的执行优先级?
这个要分二种情况:
1)如果点击的是容器类ViewGroup
dispatchTouchEvent----onInterceptTouchEvent-----onTouchListener----onTouchEvent(ACTION_UP时检测执行OnClickListener)
2)如果点击的是非容器类View,比如TextView
dispatchTouchEvent中执行onTouchListener,onTouch返回false,才执行onTouchEvent(ACTION_UP时检测执行OnClickListener)
更多相关文章
- Android长按事件和点击事件问题处理,OnItemLongClickListener和On
- Android触摸事件分发机制(1)之View
- Android事件分发机制研究
- Android 4.0 事件输入(Event Input)系统
- android视图组件容器组件与布局管理器LinearLayout
- android中的事件总线
- view对touch事件的处理
- Android Notes(06) - Touch事件分发响应机制
- android中上层是如何接收按键事件的