zoukankan      html  css  js  c++  java
  • Android Touch事件传递机制引发的血案

    尊重原创: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有关的方法

    @Override
    	public 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

    	@Override
    	public 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 started
    			mLastMotionX = 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 + ")
    mIsBeingDragged:" + 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 event
    				final 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 component
    				mLastMotionX += 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 behavior
    				final 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 menu
    				setCurrentItem(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滑动了)


  • 相关阅读:
    Java ——if条件语句 switch语句
    Java ——Scanner
    Java ——运算符
    机器学习 涉及内容、模型适用范围 、优缺点总结
    数据的爬取和分析
    文本数据处理
    机器学习【十二】使用管道模型对股票涨幅进行回归分析
    Java ——注释 命名
    Java ——类型转换 向args传递参数
    win10操作系统的安装
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/3997032.html
Copyright © 2011-2022 走看看