Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Touch事件傳遞機制引發的血案

Android Touch事件傳遞機制引發的血案

編輯:關於Android編程

 

關於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滑動了)

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved