尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38942135


关于Android Touch事件传递机制我之前也写过两篇文章,自觉得对Touche事件还是理解得比較清楚的,可是近期遇到的一个问题,让我再次对Android Touche事件进行一次学习。

我的关于Android Touche事件传递机制的文章例如以下:

http://blog.csdn.net/yuanzeyao/article/details/37961997

http://blog.csdn.net/yuanzeyao/article/details/38025165


我在这两篇文章中得出过下面结论:

1、假设一个view是clickable的,那么这个View的onTouchEvent是一定会返回true的,也就是说不论什么触摸事件都会被消费掉

2、假设一个View对于ACTION_DOWN事件没有消费掉(onTouchEvent 返回false),那么兴许的ACTION_MOVE,ACTION_UP是都不会接受到的,也就是没有机会处理这些事件,这些事件都是在父View里面给处理了

3、假设一个ViewGroup想要拦截事件(不让事件传递到子View),那么它只须要改写ViewGroup的onInterceptTouchEvent(MotionEvent ev) 方法,让他返回true,或者调用requestDisallowInterceptTouchEvent(true);

4、Android中的Touche事件是从底层向上层传递的 Activity->DecorView->ViewGroup->View


理解了上面的问题,我们就開始看看我所遇到的问题吧,

在使用SlideMenu的时候,在中的Activity中只放置一个TextView,你会发现SlideMenu无法滑动,当时通过顶部的Title能够滑动,因为对SlideMenu用的不是非常熟,当时以为是SlideMenu的哪个属性用错了,后来一直没有解决这个问题,直到一位网友说设置TextView的clickable为true就能够解决这个问题,我尝试了一下,还真行!哈哈。。。,这个里面的原因你理解了吗?假设没有理解,请继续往下看


依照我之前对Touche事件的理解,假设设置clickable,那么Touche事件肯定就被TextView给消费掉了,假设被TextView消费掉了,那么SlideMenu怎样实现滑动?要解开这个问题答案,还是看看SlideMenu的源代码吗


我们首先看看SlideMenu中CustomViewAbove和Touche有关的方法

@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {if (!mEnabled)return false;final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;if (action == MotionEvent.ACTION_DOWN && DEBUG)Log.v(TAG, "Received ACTION_DOWN");if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP|| (action != MotionEvent.ACTION_DOWN && mIsUnableToDrag)) {endDrag();return false;}switch (action) {case MotionEvent.ACTION_MOVE:try{final int activePointerId = mActivePointerId;if (activePointerId == INVALID_POINTER)break;final int pointerIndex = this.getPointerIndex(ev, activePointerId);final float x = MotionEventCompat.getX(ev, pointerIndex);final float dx = x - mLastMotionX;final float xDiff = Math.abs(dx);final float y = MotionEventCompat.getY(ev, pointerIndex);final float yDiff = Math.abs(y - mLastMotionY);if (DEBUG) Log.v(TAG, "onInterceptTouch moved to:(" + x + ", " + y + "), diff:(" + xDiff + ", " + yDiff + "), mLastMotionX:" + mLastMotionX);if (xDiff > mTouchSlop && xDiff > yDiff && thisSlideAllowed(dx)) {if (DEBUG) Log.v(TAG, "Starting drag! from onInterceptTouch");startDrag();mLastMotionX = x;setScrollingCacheEnabled(true);} else if (yDiff > mTouchSlop) {mIsUnableToDrag = true;}}catch(IllegalArgumentException e){e.printStackTrace();}break;case MotionEvent.ACTION_DOWN:mActivePointerId = ev.getAction() & ((Build.VERSION.SDK_INT >= 8) ? MotionEvent.ACTION_POINTER_INDEX_MASK : MotionEvent.ACTION_POINTER_INDEX_MASK);mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, mActivePointerId);mLastMotionY = MotionEventCompat.getY(ev, mActivePointerId);if (thisTouchAllowed(ev)) {mIsBeingDragged = false;mIsUnableToDrag = false;if (isMenuOpen() && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {mQuickReturn = true;}} else {mIsUnableToDrag = true;}break;case MotionEventCompat.ACTION_POINTER_UP:onSecondaryPointerUp(ev);break;}if (!mIsBeingDragged) {if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(ev);}return mIsBeingDragged || mQuickReturn;}

看看这种方法,这种方法里面有个逻辑就是当滑动到一定距离,就会返回true,也就是说会拦截滑动事件,第一个ACTION_DOWN肯定不会拦截。

再看看onToucheEvent.java

@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (!mEnabled)return false;//if (!mIsBeingDragged && !thisTouchAllowed(ev))//return false;if (!mIsBeingDragged && !mQuickReturn)return false;final int action = ev.getAction();if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(ev);switch (action & MotionEventCompat.ACTION_MASK) {case MotionEvent.ACTION_DOWN:/* * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged. */completeScroll();// Remember where the motion event startedmLastMotionX = mInitialMotionX = ev.getX();mActivePointerId = MotionEventCompat.getPointerId(ev, 0);break;case MotionEvent.ACTION_MOVE:if (!mIsBeingDragged) {if (mActivePointerId == INVALID_POINTER)break;final int pointerIndex = getPointerIndex(ev, mActivePointerId);final float x = MotionEventCompat.getX(ev, pointerIndex);final float dx = x - mLastMotionX;final float xDiff = Math.abs(dx);final float y = MotionEventCompat.getY(ev, pointerIndex);final float yDiff = Math.abs(y - mLastMotionY);if (DEBUG) Log.v(TAG, "onTouch moved to:(" + x + ", " + y + "), diff:(" + xDiff + ", " + yDiff + ")\nmIsBeingDragged:" + mIsBeingDragged + ", mLastMotionX:" + mLastMotionX);if ((xDiff > mTouchSlop || (mQuickReturn && xDiff > mTouchSlop / 4))&& xDiff > yDiff && thisSlideAllowed(dx)) {if (DEBUG) Log.v(TAG, "Starting drag! from onTouch");startDrag();mLastMotionX = x;setScrollingCacheEnabled(true);} else {if (DEBUG) Log.v(TAG, "onTouch returning false");return false;}}if (mIsBeingDragged) {// Scroll to follow the motion eventfinal int activePointerIndex = getPointerIndex(ev, mActivePointerId);if (mActivePointerId == INVALID_POINTER) {break;}final float x = MotionEventCompat.getX(ev, activePointerIndex);final float deltaX = mLastMotionX - x;mLastMotionX = x;float oldScrollX = getScrollX();float scrollX = oldScrollX + deltaX;final float leftBound = getLeftBound();final float rightBound = getRightBound();if (scrollX < leftBound) {scrollX = leftBound;} else if (scrollX > rightBound) {scrollX = rightBound;}// Don't lose the rounded componentmLastMotionX += scrollX - (int) scrollX;scrollTo((int) scrollX, getScrollY());pageScrolled((int) scrollX);}break;case MotionEvent.ACTION_UP:if (mIsBeingDragged) {final VelocityTracker velocityTracker = mVelocityTracker;velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(velocityTracker, mActivePointerId);final int scrollX = getScrollX();//final int widthWithMargin = getWidth();//final float pageOffset = (float) (scrollX % widthWithMargin) / widthWithMargin;// TODO test this. should get better flinging behaviorfinal float pageOffset = (float) (scrollX - getDestScrollX(mCurItem)) / getBehindWidth();final int activePointerIndex = getPointerIndex(ev, mActivePointerId);if (mActivePointerId != INVALID_POINTER) {final float x = MotionEventCompat.getX(ev, activePointerIndex);final int totalDelta = (int) (x - mInitialMotionX);int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);setCurrentItemInternal(nextPage, true, true, initialVelocity);} else {setCurrentItemInternal(mCurItem, true, true, initialVelocity);}mActivePointerId = INVALID_POINTER;endDrag();} else if (mQuickReturn && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {// close the menusetCurrentItem(1);endDrag();}break;case MotionEvent.ACTION_CANCEL:if (mIsBeingDragged) {setCurrentItemInternal(mCurItem, true, true);mActivePointerId = INVALID_POINTER;endDrag();}break;case MotionEventCompat.ACTION_POINTER_DOWN: {final int index = MotionEventCompat.getActionIndex(ev);final float x = MotionEventCompat.getX(ev, index);mLastMotionX = x;mActivePointerId = MotionEventCompat.getPointerId(ev, index);break;}case MotionEventCompat.ACTION_POINTER_UP:onSecondaryPointerUp(ev);int pointerIndex = this.getPointerIndex(ev, mActivePointerId);if (mActivePointerId == INVALID_POINTER)break;mLastMotionX = MotionEventCompat.getX(ev, pointerIndex);break;}return true;}

我们重点观察ACTION_DWON事件,对于ACTION_DWON事件,SlideMenu是没有拦截的,所以传递到了TextView,因为默认TextView是没有clickable的,所以是不会消费这个事件,假设TextView不消费,那么事件就传递到了SlideMenu,可是我们发如今SlideMenu中也没有消费这个事件,还记得我们上面的结论2吗,依据结论2,我们知道后面的事件是传递只是来的,所以导致了SlideMenu无法滑动。


假设我们设置了clickable,那么第一个ACTION_DOWN就被TextView处理了,所以后面每一个事件都会传递到TextView(前提是不被拦截,实际结果是被拦截,并被SlideMenu处理,所以SlideMenu滑动了)


更多相关文章

  1. Android(安卓)UI开发第二十八篇——Fragment中使用左右滑动菜单
  2. Android实现上下滑动效果
  3. Seekbar thumb滑动时上下显示不全,或者左右显示不全的解决办法
  4. android WebView知识
  5. Android(安卓)滑动侧边栏(Sliding Menu)第一种实现 - 2 手动滚动+
  6. Android(Java):Android(安卓)事件分发机制
  7. android 回车键事件编程
  8. Android(安卓)事件分发机制源码
  9. Android(安卓)开发艺术探索笔记 前四章

随机推荐

  1. View类的XML属性、相关方法及说明
  2. Android中的自动朗读(TTS)
  3. Android(安卓)UndoBar介绍
  4. android Sqlite多线程访问异常解决方案
  5. android的style控制Theme
  6. Android(安卓)Studio
  7. android使用全局变量的两种方法
  8. Android(安卓)UI开发第二十篇——仿launc
  9. Android的绘制文本对象FontMetrics的介绍
  10. Android(安卓)-- 再来一发Intent