Android swipe conflict handling

There are two common
  •      One control slides horizontally and the other slides vertically. For example: Similar to ViewPager, each page is a ListView, but we don't need to deal with the sliding processing, the ViewPager has been processed internally.
  •      One control slides vertically, and the other control slides vertically. For example: ListView is wrapped in ScollView, which also needs to be understood and mastered.



 

Android has a built-in Scoller for progressive swiping.
  •  Create a Scroller object: Scroller mScroller = new Scroller(context);
  •  Override the computeScroll() method;
  •  Finally, call the startScroll method in our smoothScrollTo method;
Scroller mScroller = new Scroller(context);
@Override
public void computeScroll() {
    if (mScroller.computeScrollOffset()){
        scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
        postInvalidate();
    }
}
//Here smoothScrollTo achieves smoothness in the x direction
public void smoothScrollTo(int destX,int destY){
    int scrollX=getScrollX();
    int deltaX=destX-scrollX;
    mScroller.startScroll(scrollX,0,deltaX,0,1000);//The formal parameters of the startScroll function respectively indicate: the x coordinate of the starting position, the y coordinate of the starting position, the distance to be moved in the x direction, and the distance to be moved in the y direction. The distance moved and the time it takes for the entire sliding process to complete.
    invalidate();
}
 
Scroller.computeScrollOffset(): This method returns true, indicating that the scrolling has not ended, and the scrolling should continue, and false indicates that the sliding has ended.
scrollTo: Note that the essence of the application here is scrollTo(...), so the View is required to be a View with content, and the content of the smooth movement is also the change of the position of the View itself.
postInvalidate(): used in non-UI threads, and finally calls invalidate() internally (similarly, invalidate is used in UI threads).
startScroll: When we construct a Scroller object and call its startScroll method, nothing is actually done inside the Scroller, it just saves a few parameters we pass.
principle:
     How does the Scroller make the View slide gradually? The answer is the invalidate method below the startScroll method. The invalidate method will cause the View to be redrawn, and the computeScroll method will be called in the View's draw method. The computeScroll method is an empty implementation in the View, so we need to implement it ourselves. The computeScroll method is already implemented in our code, so that's what makes it move.
Summarize:
     The Scroller itself cannot realize the sliding of the View. We need to use the computeScroll method of the View and let it constantly refresh and redraw.
 
Sliding conflict resolution:
  •      external interception
    •       Via onInterceptTouchEvent(MotionEvent event)
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
  boolean intercepted = false;
  int x = (int) event.getX();
  int y = (int) event.getY();
  switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN:
        intercepted = false;
        break;
  case MotionEvent.ACTION_MOVE:
  if (parent container needs current click event) {
     intercepted = true;
  } else {
     intercepted = false;
  }
  break;
  case MotionEvent.ACTION_UP:
  intercepted = false;
  break;
  default:
        break;            
  }
  mLastXIntercept = x;
  mLastYIntercept = y;
  return intercepted;
}
/*
  Notice:
  1. ACTION_DOWN must return false, do not intercept it, otherwise according to the View event distribution mechanism, the subsequent ACTION_MOVE and ACTION_UP events will be handled by the parent View by default!
  2. In principle, ACTION_UP should also return false. If it returns true, and the sliding event is handed over to the sub-View, then the sub-View will not receive the ACTION_UP event, and the onClick event of the sub-View will not be triggered.
  The parent View is different. If the parent View starts to intercept events in ACTION_MOVE, the subsequent ACTION_UP will also be handed over to the parent View for processing by default.
  */
 
  •      internal interception
    •      Via dispatchTouchEvent(MotionEvent event)
    •      The internal interception method means that the parent container does not intercept any events, and all events are passed to the child element. If the child element needs this event, it is directly consumed, otherwise it is handed over to the parent container for processing. This method is similar to Android's event distribution mechanism. Inconsistent, it needs to cooperate with the requestDisallowInterceptTouchEvent method to work properly, which is more complicated to use than the external interception method.
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
  int x= (int) ev.getX();
  int y = (int) ev.getY ();
  switch (ev.getAction()){
  case MotionEvent.ACTION_DOWN:
        parent.requestDisallowInterceptTouchEvent(true);
        break;
  case MotionEvent.ACTION_MOVE:
  int deltaX=x-mLastX;
  int deltaY=y-mLastY;
  if (parent container needs such click event){
  parent.requestDisallowInterceptTouchEvent(false);
  }
        break;
  case MotionEvent.ACTION_UP:
        break;
  default:
        break;
  }
  mLastX=x;
  mLastY=y;
  return super.dispatchTouchEvent(ev);
}
/*
The internal interception method requires that the parent View cannot intercept the ACTION_DOWN event. Since ACTION_DOWN is not controlled by the FLAG_DISALLOW_INTERCEPT flag, once the parent container intercepts ACTION_DOWN, all events will not be passed to the child View.
*/
 
 
ViewPager source code analysis
Borrowing and understanding articles ( http://blog.csdn.net/huachao1001/article/details/51654692 )
ViewPager's sliding conflict handling:
     We know that ViewGroup decides whether to intercept touch events in the onInterceptTouchEvent function, so let's learn the onInterceptTouchEvent function of ViewPager.
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
 
    //1. touch action
    final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
 
    //2. Always pay attention to whether the touch has ended
    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
        //3. Release the drag.
        if (DEBUG) Log.v(TAG, "Intercept done!");
        //4. Reset some variables related to judging whether to intercept touch
        resetTouch();
        //5. The touch ends, no need to intercept
        return false;
    }
 
    //6. If the current event is not a press event, let's judge whether it is dragging and switching pages
    if (action != MotionEvent.ACTION_DOWN) {
        //7. If you are currently dragging and switching pages, intercept the event directly, and no need to make interception judgment later
        if (mIsBeingDragged) {
            if (DEBUG) Log.v(TAG, "Intercept returning true!");
            return true;
        }
        //8. If it is marked as not allowing drag and drop to switch pages, we "let go" of all touch events
        if (mIsUnableToDrag) {
            if (DEBUG) Log.v(TAG, "Intercept returning false!");
            return false;
        }
    }
    //9. Process according to different actions
    switch (action) {
        //10. If it is a finger movement operation
        case MotionEvent.ACTION_MOVE: {
 
            //11. If the code can be executed here, it means that mIsBeingDragged==false, otherwise, the execution has ended at the 7th comment
 
            //12. Use touch point Id, mainly to handle multi-touch
            final int activePointerId = mActivePointerId;
            if (activePointerId == INVALID_POINTER) {
                //13. If the current touch point id is not a valid Id, no further processing is required
                break;
            }
            //14. To distinguish different fingers according to the id of the touch point, we only need to focus on one finger
            final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
            //15. According to the serial number of this finger, get the x coordinate corresponding to this finger
            final float x = MotionEventCompat.getX(ev, pointerIndex);
            //16. The distance moved in the x-axis direction
            final float dx = x - mLastMotionX;
            //17. The absolute value of the moving distance in the x-axis direction
            final float xDiff = Math.abs(dx);
            //18. For the same reason, refer to comments 16 and 17
            final float y = MotionEventCompat.getY(ev, pointerIndex);
            final float yDiff = Math.abs(y - mInitialMotionY);
            if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
 
            //19. Determine whether the currently displayed page can be swiped, and if it can be slid, throw the event to the currently displayed page for processing
            //isGutterDrag is to determine whether to move within the gap between two pages
            //canScroll is to determine whether the page can be swiped
            if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&
                    canScroll(this, false, (int) dx, (int) x, (int) y)) {
                mLastMotionX = x;
                mLastMotionY = y;
                //20. Mark ViewPager not to intercept events
                mIsUnableToDrag = true;
                return false;
            }
            //21. If the x moving distance is greater than the minimum distance and the slope is less than 0.5, it means dragging in the horizontal direction
            if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
                if (DEBUG) Log.v(TAG, "Starting drag!");
                //22. Horizontal movement requires ViewPager to intercept
                mIsBeingDragged = true;
                //23. If the ViewPager has a parent View, it also applies to the parent View to pass the touch event to the ViewPager
                requestParentDisallowInterceptTouchEvent(true);
                //24. Set the scroll state
                setScrollState(SCROLL_STATE_DRAGGING);
                //25. Save the current position
                mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :
                        mInitialMotionX - mTouchSlop;
                mLastMotionY = y;
                //26. Enable caching
                setScrollingCacheEnabled(true);
            } else if (yDiff > mTouchSlop) {//27. Otherwise, it means vertical movement
                if (DEBUG) Log.v(TAG, "Starting unable to drag!");
                //28. Movement in the vertical direction does not intercept touch events
                mIsUnableToDrag = true;
            }
            if (mIsBeingDragged) {
                // 29. Swipe with your finger
                if (performDrag(x)) {
                    ViewCompat.postInvalidateOnAnimation(this);
                }
            }
            break;
        }
        //30. If the finger is pressed
        case MotionEvent.ACTION_DOWN: {
 
            //31. Record the pressed point position
            mLastMotionX = mInitialMotionX = ev.getX();
            mLastMotionY = mInitialMotionY = ev.getY ();
            //32. The finger number corresponding to the first ACTION_DOWN event is 0
            mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
            //33. Reset allows drag and drop to switch pages
            mIsUnableToDrag = false;
            //34. The marker starts scrolling
            mIsScrollStarted = true;
            //35. Manually call to calculate the offset of the sliding
            mScroller.computeScrollOffset();
            //36. If the current scroll state is placing the page to the final position,
            //and the current position is far enough from the final position
            if (mScrollState == SCROLL_STATE_SETTLING &&
                    Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {
                //37. If the user's finger presses at this time, the slide will be suspended immediately
                mScroller.abortAnimation();
                mPopulatePending = false;
                populate();
                mIsBeingDragged = true;
                //38. If the ViewPager has a parent View, it also applies to the parent View to pass the touch event to the ViewPager
                requestParentDisallowInterceptTouchEvent(true);
                //39. Set the current state to be dragging
                setScrollState(SCROLL_STATE_DRAGGING);
            } else {
                //40. End scrolling
                completeScroll(false);
                mIsBeingDragged = false;
            }
 
            if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY
                    + " mIsBeingDragged=" + mIsBeingDragged
                    + "mIsUnableToDrag =" + mIsUnableToDrag);
            break;
        }
 
        case MotionEventCompat.ACTION_POINTER_UP:
            onSecondaryPointerUp(ev);
            break;
    }
 
    //41. Add speed tracking
    if (mVelocityTracker == null) {
        mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(ev);
 
 
    //42. We will intercept the event only when the current page is dragged and switched
    return mIsBeingDragged;
}
 
Let's see how ViewPager decides whether to intercept or not to intercept. It can be seen from the source code, but when the slope is less than 0.5, it must be intercepted, otherwise it will not be intercepted. What is the slope? High school mathematics shows that in the first quadrant, the closer the line is to the y-axis, the greater the slope, and the closer to the x-axis the line has a smaller slope. Let’s first look at the simple diagram:
(What is the slope: the slope is the degree of inclination, the slope is generally expressed by k, the slope k value is the tangent of the angle between the straight line and the positive direction of the x-axis, if any two points on the straight line are (x1, y1), (x2, y2) Then the slope of the straight line k=(y2-y1)/(x2-x1). The straight line is parallel to the y-axis, the slope does not exist, it is parallel to the x-axis, and the slope is 0)


 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326219473&siteId=291194637