Android Touch event transfer mechanism, androidtouch
Respect Originality:Http://blog.csdn.net/yuanzeyao/article/details/38942135
I have also written two articles about the Android Touch event transfer mechanism. I think the Touche event is still well understood, but I have encountered a problem recently, let me learn about the Android Touche event again.
My article on Android Touche event transfer mechanism is as follows:
Http://blog.csdn.net/yuanzeyao/article/details/37961997
Http://blog.csdn.net/yuanzeyao/article/details/38025165
I have come to the following conclusions in these two articles:
1. If a view is clickable, The onTouchEvent of this View will certainly return true, that is, any touch event will be consumed.
2. If a View is not consumed for the ACTION_DOWN event (onTouchEvent returns false), the subsequent ACTION_MOVE and ACTION_UP events will not be accepted, that is, there is no chance to process these events, these events are handled in the parent View.
3. If a ViewGroup wants to intercept an event (not to pass the event to the subview), it only needs to rewrite the onInterceptTouchEvent (MotionEvent ev) method of the ViewGroup so that it returns true, or call requestDisallowInterceptTouchEvent (true );
4. The Touche event in Android is the Activity passed from the underlying layer to the upstream layer-> DecorView-> ViewGroup-> View
After understanding the problem above, let's start to look at the problem I encountered,
When using SlideMenu, place only one TextView in the Activity, and you will find that SlideMenu cannot be moved. At that time, slide can be made through the Title on the top, because it is not very familiar with SlideMenu, at that time, I thought that the SlideMenu attribute was used incorrectly and the problem was not solved until a netizen said that setting the TextView clickable to true could solve the problem. I tried it, really good! Haha... do you understand the reasons? If you do not understand it, continue to read it.
According to my previous understanding of Touche events, if clickable is set, the Touche event will be consumed by TextView. If TextView is consumed, how can slide the SlideMenu? To solve this problem, do you still need to check the SlideMenu source code?
First, let's look at the methods related to mmviewabove and Touche in SlideMenu.
@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;}
Look at this method. The logic in this method is that when sliding to a certain distance, true will be returned, that is, the sliding event will be intercepted, and the first ACTION_DOWN will certainly not be intercepted.
Let's take a look at 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;}
We focus on the ACTION_DWON event. For ACTION_DWON events, SlideMenu is not intercepted, so it is passed to TextView. Because TextView does not have clickable by default, it will not consume this event, if TextView is not consumed, the event is passed to SlideMenu, but we find that this event is not consumed in SlideMenu. Do you still remember conclusion 2 above? According to conclusion 2, we know that the subsequent events cannot be passed, so SlideMenu cannot be moved.
If clickable is set, the first ACTION_DOWN is processed by TextView. Therefore, each subsequent event is passed to TextView (provided that the event is not blocked, but the actual result is intercepted, it is processed by SlideMenu, so SlideMenu slides)
Does the android onclick event conflict with the ontouch event?
The order and Association of the onTouch, onClick, and onLongClick events of a Button are as follows:
1. onTouch returns false.
First, the onTouch event down event occurs. At this time, if you press for a long time, the onLongClick event is triggered;
Next, the onTouch event's up event occurs, the up is complete, and The onClick event is triggered.
2. onTouch returns true.
First, the onTouch event's down event occurs, and then the onTouch event's up event occurs. During this period, the onClick and onLongClick events are not triggered.
3. onTouch: true is returned for down, and false is returned for up: the result is the same as 2.
Mechanism Analysis:
In the onTouch event: the return value of the down event indicates whether the event is a click event (false is returned; true is returned, not recorded as a click event), and The up event indicates the end time of the event, that is, to determine whether a long press is used.
As long as true is returned when the event is down, the system will not record this event as a click event, so the onClick or onLongClick event will not be triggered. Therefore, even if false is returned when the event is up, the system will not trigger the onClick event any more.
4. onTouch: false for down and true for up:
First, the onTouch event's down event occurs. At this time:
Long press to trigger the onLongClick event, and then the onTouch event's up event occurs.
Short Press: triggers the onTouch up event first. after a certain period of time, the onLongClick event is automatically triggered.
Mechanism Analysis:
In the onTouch event: the return value of the down event indicates whether the event is a click event (false is returned; true is returned, not recorded as a click event), and The up event indicates the end time of the event, that is, to determine whether a long press is used.
When "down" returns false, this event is marked as a click event, and "up" returns true, it indicates that the event has not ended, that is, it has been pressed for a long time, after reaching the long press critical time, the onClick event is not triggered.
Android onTouch event
Yes. set two global variables, for example, global int a and B;
Then in ontouch
Switch (event. getAction ()){
Case MotionEvent. ACTION_DOWN:
A = 1;
Break;
Case MotionEvent. ACTION_MOVE:
B = 2;
Break;
Default:
Break;
}
In this way, when the finger is put down, a = 1, and then B = 2 when the finger is moved;