Drag and Slide of ViewPager Source Parsing

Summary

The sliding of a ViewGroup must be related to its dispatchTouchEvent(), onInterceptTouchEvent(), onTouchEvent(). ViewPager rewrote the latter two. Let's look at them one by one. First of all, there are two ways for ViewPager to generate view movement based on gesture. One is to drag with finger when MOVE, the other is to slide to the specified page after UP, and the sliding is realized by Scroller + computeScroll().

onInterceptTouchEvent()

Viewpager has an mScrollState member that maintains the status of the ViewPager's current page, which may be assigned three values.

/**
     * Indicates that the pager is in an idle, settled state. The current page
     * is fully in view and no animation is in progress.
     */
    //Current page is idle, no animation
    public static final int SCROLL_STATE_IDLE = 0;

    /**
     * Indicates that the pager is currently being dragged by the user.
     */
//Being dragged
    public static final int SCROLL_STATE_DRAGGING = 1;

    /**
     * Indicates that the pager is in the process of settling to a final position.
     */
    //Moving towards the final position
    public static final int SCROLL_STATE_SETTLING = 2;

As the onInterceptTouchEvent() comment says, the method is simply to determine whether we should intercept the Touch event, and scrolling is left to onTouchEvent(). Because there are break s in every branch of switch, I changed the order of source code and put DOWN in front of MOVE, which is more clear.

   @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        /*
         * This method JUST determines whether we want to intercept the motion.
         * If we return true, onMotionEvent will be called and we do the actual
         * scrolling there.
         */

        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;

        // Always take care of the touch gesture being complete.
      //If a set of gestures ends, return false
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            // Release the drag.
            if (DEBUG) Log.v(TAG, "Intercept done!");
            resetTouch();
            return false;
        }

        // Nothing more to do here if we have decided whether or not we
        // are dragging.
        if (action != MotionEvent.ACTION_DOWN) {
          //If it's being drag ged, intercept
            if (mIsBeingDragged) {
                if (DEBUG) Log.v(TAG, "Intercept returning true!");
                return true;
            }
          //No drag, no interception
            if (mIsUnableToDrag) {
                if (DEBUG) Log.v(TAG, "Intercept returning false!");
                return false;
            }
        }

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                /*
                 * Remember location of down touch.
                 * ACTION_DOWN always refers to pointer index 0.
                 */
              //Re-assign these four variables to indicate the beginning of a set of gestures
                mLastMotionX = mInitialMotionX = ev.getX();
                mLastMotionY = mInitialMotionY = ev.getY();
                //Get the id of the first touch point
                mActivePointerId = ev.getPointerId(0);
                //Set allowable drag-and-drop to false
                mIsUnableToDrag = false;
                //Mark start scrolling
                mIsScrollStarted = true;
                //This Scroller was created in initViewPager(), where the x,y values in //Scroller are calculated manually.
                mScroller.computeScrollOffset();
              //If we are moving towards the final state at this time, and there is still a certain distance from the final position.
                if (mScrollState == SCROLL_STATE_SETTLING
                        && Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {
                    // Let the user 'catch' the pager as it animates.
                  //Let users grab the Pager

                  //Stop scrolling
                    mScroller.abortAnimation();
                  //???
                    mPopulatePending = false;
                  //Update cached page information (mCurItem changes when scrolling?)
                    populate();
                  //Represents dragging
                    mIsBeingDragged = true;
                  //Parent ViewGroup is not allowed to intercept
                    requestParentDisallowInterceptTouchEvent(true);
                  //Setting up a new state
                    setScrollState(SCROLL_STATE_DRAGGING);
                } else {
                  //What I understand here is that we are approaching the final state and the distance is small enough to interfere with the motion.
                    completeScroll(false);
                    mIsBeingDragged = false;
                }
                ....
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                /*
                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
                 * whether the user has moved far enough from his original down touch.
                 */
                //The annotations are very clear. If you can enter this place, it means mIsBeingDragged == false
                //Here, check if the user has moved far enough from the original location to assign a value to mIsBeingDragged


                //id of the first touch point
                final int activePointerId = mActivePointerId;
                if (activePointerId == INVALID_POINTER) {
                    // If we don't have a valid id, the touch down wasn't on content.
                    break;
                }
                //I don't quite understand. It looks like I made a conversion for the first touch point id.
                final int pointerIndex = ev.findPointerIndex(activePointerId);
               //Touch point abscissa
                final float x = ev.getX(pointerIndex);
                //Transverse migration
                final float dx = x - mLastMotionX;
                //Absolute value of lateral migration
                final float xDiff = Math.abs(dx);
                //portrait
                final float y = ev.getY(pointerIndex);
                final float yDiff = Math.abs(y - mInitialMotionY);
                ...

                  //This means that if the sub-View can slide in this area, it is handed over to the sub-View and not intercepted.
                  //The source code for canScroll is posted at the back, looking for slidable ones in the sub-View
                if (dx != 0 && !isGutterDrag(mLastMotionX, dx)
                        && canScroll(this, false, (int) dx, (int) x, (int) y)) {
                    // Nested view has scrollable area under this point. Let it be handled there.
                    mLastMotionX = x;
                    mLastMotionY = y;
                    mIsUnableToDrag = true;
                    return false;
                }
                //If the absolute value of lateral migration is greater than the minimum value and yDiff/xDiff < 0.5f
                if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
                    if (DEBUG) Log.v(TAG, "Starting drag!");
                    //Intercept!
                    mIsBeingDragged = true;
                    requestParentDisallowInterceptTouchEvent(true);
                    setScrollState(SCROLL_STATE_DRAGGING);
                   //Save the current location
                    mLastMotionX = dx > 0
                            ? mInitialMotionX + mTouchSlop : mInitialMotionX - mTouchSlop;
                    mLastMotionY = y;
                    setScrollingCacheEnabled(true);
                } else if (yDiff > mTouchSlop) {
                    //If enough distance is moved lengthwise, no interception is allowed.

                    // The finger has moved enough in the vertical
                    // direction to be counted as a drag...  abort
                    // any attempt to drag horizontally, to work correctly
                    // with children that have scrolling containers.
                    if (DEBUG) Log.v(TAG, "Starting unable to drag!");
                    mIsUnableToDrag = true;
                }
              //
                if (mIsBeingDragged) {
                    // Scroll to follow the motion event
                    //If dragging is possible, dragging is produced here, which is very important. This is analyzed later.
                    if (performDrag(x)) {
                        ViewCompat.postInvalidateOnAnimation(this);
                    }
                }
                break;
            }

            case MotionEventCompat.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                break;
        }

        ...
        return mIsBeingDragged;
    }

Traversing through the sub-View, the contact is within the boundaries of the sub-View, and the sub-View can slide back to true

    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        if (v instanceof ViewGroup) {
            final ViewGroup group = (ViewGroup) v;
            final int scrollX = v.getScrollX();
            final int scrollY = v.getScrollY();
            final int count = group.getChildCount();
            // Count backwards - let topmost views consume scroll distance first.
            for (int i = count - 1; i >= 0; i--) {
                // TODO: Add versioned support here for transformed views.
                // This will not work for transformed views in Honeycomb+
                final View child = group.getChildAt(i);
                if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
                        && y + scrollY >= child.getTop() && y + scrollY < child.getBottom()
                        && canScroll(child, true, dx, x + scrollX - child.getLeft(),
                                y + scrollY - child.getTop())) {
                    return true;
                }
            }
        }

        return checkV && ViewCompat.canScrollHorizontally(v, -dx);
    }

performDrag()

The previous onInterceptTouchEvent() judged many situations, basically judging drag according to the situation, and then assigning the variable mIsBeingDragged to represent whether to intercept the next series of gestures. At the end of MOVE, when drag is available, we will go into this method to let the page follow the finger gesture.

    private boolean performDrag(float x) {
        boolean needsInvalidate = false;

        final float deltaX = mLastMotionX - x;
        mLastMotionX = x;

        float oldScrollX = getScrollX();
        //ViewPager's view abscissa
        float scrollX = oldScrollX + deltaX;
        final int width = getClientWidth();
        //Sub-View left boundary and sub-View right boundary
        float leftBound = width * mFirstOffset;
        float rightBound = width * mLastOffset;

        boolean leftAbsolute = true;
        boolean rightAbsolute = true;
        //Current first and last page information
        final ItemInfo firstItem = mItems.get(0);
        final ItemInfo lastItem = mItems.get(mItems.size() - 1);
        //If the first page information is not item 0 of the data, update the leftBound
        if (firstItem.position != 0) {
            leftAbsolute = false;
            leftBound = firstItem.offset * width;
        }
      //Empathy
        if (lastItem.position != mAdapter.getCount() - 1) {
            rightAbsolute = false;
            rightBound = lastItem.offset * width;
        }
        //boundary condition
        if (scrollX < leftBound) {
            if (leftAbsolute) {
                float over = leftBound - scrollX;
                needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width);
            }
            scrollX = leftBound;
        } else if (scrollX > rightBound) {
            if (rightAbsolute) {
                float over = scrollX - rightBound;
                needsInvalidate = mRightEdge.onPull(Math.abs(over) / width);
            }
            scrollX = rightBound;
        }
        // Don't lose the rounded component
        mLastMotionX += scrollX - (int) scrollX;
      //Sliding View
        scrollTo((int) scrollX, getScrollY());
        //Important methods
        pageScrolled((int) scrollX);

        return needsInvalidate;
    }

pageScrolled()

The performDrag() method slides the view of the ViewPager (through the scrollTo() method) and calls the method. Now let's look at this method. Overall, this approach does a number of things:

1. The current page information is obtained from scrollX of the view.

2. The ratio of sliding distance and pixels are calculated.

3.onPageScrolled(currentPage, pageOffset, offsetPixels)

Let's say that if sliding causes two Pages to be displayed in the ViewPager display area, infoForCurrentScrollPosition() returns the ItemInfo on the left.

    private boolean pageScrolled(int xpos) {
        //The incoming parameter refers to the distance from which the ViewPager view slides.
        ...
         //Get the page information that should be displayed according to scrollX
        final ItemInfo ii = infoForCurrentScrollPosition();
        final int width = getClientWidth();
        final int widthWithMargin = width + mPageMargin;
        final float marginOffset = (float) mPageMargin / width;
        //The position of the current page (this position refers to the position in the data list)
        final int currentPage = ii.position;
        //Here we calculate the ratio of the sliding distance of the current page view to the width of the current page.
        //If the second item is 1, it represents the ratio of the sliding distance (and page width) of the current page view.
        final float pageOffset = (((float) xpos / width) - ii.offset)
                / (ii.widthFactor + marginOffset);
      //Pixels representing view sliding
        final int offsetPixels = (int) (pageOffset * widthWithMargin);

        mCalledSuper = false;
        onPageScrolled(currentPage, pageOffset, offsetPixels);
        if (!mCalledSuper) {
            throw new IllegalStateException(
                    "onPageScrolled did not call superclass implementation");
        }
        return true;
    }

onPageScrolled(currentPage, pageOffset, offsetPixels)

This method is called in many scrolling places. We can rewrite this method to achieve some animation effects. Throughout this approach, several things have been done:

1. Scroll DecorView

2. The onPageScrolled() of the callback interface is the interface we added ourselves.

3. Realizing animation

protected void onPageScrolled(int position, float offset, int offsetPixels) {
        // Offset any decor views if needed - keep them on-screen at all times.
        //1
        if (mDecorChildCount > 0) {
            final int scrollX = getScrollX();
            int paddingLeft = getPaddingLeft();
            int paddingRight = getPaddingRight();
            final int width = getWidth();
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = getChildAt(i);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (!lp.isDecor) continue;

                final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
                int childLeft = 0;
                switch (hgrav) {
                    default:
                        childLeft = paddingLeft;
                        break;
                    case Gravity.LEFT:
                        childLeft = paddingLeft;
                        paddingLeft += child.getWidth();
                        break;
                    case Gravity.CENTER_HORIZONTAL:
                        childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
                                paddingLeft);
                        break;
                    case Gravity.RIGHT:
                        childLeft = width - paddingRight - child.getMeasuredWidth();
                        paddingRight += child.getMeasuredWidth();
                        break;
                }
                childLeft += scrollX;

                final int childOffset = childLeft - child.getLeft();
                if (childOffset != 0) {
                    child.offsetLeftAndRight(childOffset);
                }
            }
        }
        //2
        dispatchOnPageScrolled(position, offset, offsetPixels);
        //3
        if (mPageTransformer != null) {
            final int scrollX = getScrollX();
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = getChildAt(i);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                if (lp.isDecor) continue;
                final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
                mPageTransformer.transformPage(child, transformPos);
            }
        }

        mCalledSuper = true;
    }

To sum up, mIsBeingDragged is assigned in onInterceptTouchEvent() according to different conditions. If it is slidable in MOVE, scrollTo is called to slide the view to form drag effect. Then pageScrolled() gets the information and offset of the current page and passes it to onPageScrolled(), onPageScrolled() moves Decor View and calls back the interface. Generate animation.

onInterceptTouchEvent() produces drag effect, but the main thing is to judge whether to intercept. Next, let's look at onTouchEvent().

onTouchEvent()

Throughout the whole method, MOVE is still dragging, while UP calculates the next page nextPage according to the current page, offset of the current page, speed and lateral movement distance, and then calls setCurrent Item Internal () to generate sliding.

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //Some judgments, omissions
        ...

        mVelocityTracker.addMovement(ev);

        final int action = ev.getAction();
        boolean needsInvalidate = false;

        switch (action & MotionEventCompat.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                //Immediately set x and y in Scroller to final values
                mScroller.abortAnimation();
                mPopulatePending = false;
                //Update page information that needs to be cached according to mCurIndex
                populate();

                // Remember where the motion event started
                //Record
                mLastMotionX = mInitialMotionX = ev.getX();
                mLastMotionY = mInitialMotionY = ev.getY();
                mActivePointerId = ev.getPointerId(0);
                break;
            }
            case MotionEvent.ACTION_MOVE:
                //If it's not in drag (this may be because there's no gesture-consuming sub-View, so go back and let the ViewPager process it)
                if (!mIsBeingDragged) {
                    final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                    if (pointerIndex == -1) {
                        // A child has consumed some touch events and put us into an inconsistent
                        // state.
                        needsInvalidate = resetTouch();
                        break;
                    }
                    //Calculating lateral and vertical migration
                    final float x = ev.getX(pointerIndex);
                    final float xDiff = Math.abs(x - mLastMotionX);
                    final float y = ev.getY(pointerIndex);
                    final float yDiff = Math.abs(y - mLastMotionY);
                    //If the lateral migration is large enough and the lateral migration is larger than the longitudinal migration, drag can be started.
                    if (xDiff > mTouchSlop && xDiff > yDiff) {
                        if (DEBUG) Log.v(TAG, "Starting drag!");
                        mIsBeingDragged = true;
                        requestParentDisallowInterceptTouchEvent(true);
                        mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
                                mInitialMotionX - mTouchSlop;
                        mLastMotionY = y;
                        setScrollState(SCROLL_STATE_DRAGGING);
                        setScrollingCacheEnabled(true);

                        // Disallow Parent Intercept, just in case
                        ViewParent parent = getParent();
                        if (parent != null) {
                            parent.requestDisallowInterceptTouchEvent(true);
                        }
                    }
                }
                // Not else! Note that mIsBeingDragged can be set above.
                //If you can drag
                if (mIsBeingDragged) {
                    // Scroll to follow the motion event
                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                    final float x = ev.getX(activePointerIndex);
                    //Implementing drag, which has been analyzed on performance Drag
                    needsInvalidate |= performDrag(x);
                }
                break;
            case MotionEvent.ACTION_UP:
                //If it is dragged up
                if (mIsBeingDragged) {
                    //Calculating x-velocity
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
                            velocityTracker, mActivePointerId);
                    mPopulatePending = true;
                    //ViewPager display width
                    final int width = getClientWidth();
                    //View Lateral Sliding Distance
                    final int scrollX = getScrollX();
                    //Calculate the current page information according to scrollX.
                    final ItemInfo ii = infoForCurrentScrollPosition();
                    //Marginal proportions
                    final float marginOffset = (float) mPageMargin / width;
                    //Location of the current page in the data list
                    final int currentPage = ii.position;
                    //Calculate the current page offset
                    final float pageOffset = (((float) scrollX / width) - ii.offset)
                            / (ii.widthFactor + marginOffset);

                    //Transverse migration
                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                    final float x = ev.getX(activePointerIndex);
                    final int totalDelta = (int) (x - mInitialMotionX);
                   //Determine the page where the final state is after up
                    int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
                            totalDelta);
                  //Important. Here's the key to sliding.
                    setCurrentItemInternal(nextPage, true, true, initialVelocity);

                    needsInvalidate = resetTouch();
                }
                break;
            ...
            case MotionEventCompat.ACTION_POINTER_DOWN: {
                final int index = MotionEventCompat.getActionIndex(ev);
                final float x = ev.getX(index);
                //Multi-touch, change another finger and update mLastMotionX and mActivePointerId
                mLastMotionX = x;
                mActivePointerId = ev.getPointerId(index);
                break;
            }
            case MotionEventCompat.ACTION_POINTER_UP:
                //Apparently the next finger is raised by multi-touch, to update mLastMotionX
                onSecondaryPointerUp(ev);
                mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
                break;
        }
        if (needsInvalidate) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
        return true;
    }

setCurrentItemInternal()

The purpose of this method is to determine whether onMeasure() or scrollToItem() should be given to complete the page's set.

    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
       //Some robustness judgments, omitted
        ...
        final int pageLimit = mOffscreenPageLimit;
       //With regard to skipping sliding, we will not update the pages involved in the process of skipping.
        if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
            // We are doing a jump by more than one page.  To avoid
            // glitches, we want to keep all current pages in the view
            // until the scroll ends.
            for (int i = 0; i < mItems.size(); i++) {
                mItems.get(i).scrolling = true;
            }
        }
        final boolean dispatchSelected = mCurItem != item;
        //I remember when we started our last article, we were going to get into this branch, but it's not going to happen here.
        if (mFirstLayout) {
            mCurItem = item;
            if (dispatchSelected) {
                dispatchOnPageSelected(item);
            }
            requestLayout();
        } else {//Here we update the page information and slide to the target page.
            populate(item);
            scrollToItem(item, smoothScroll, velocity, dispatchSelected);
        }
    }

scrollToItem()

Depending on whether it is smoothScroll for different sliding, smoothScrollTo() or direct scrollTo().

    private void scrollToItem(int item, boolean smoothScroll, int velocity,
            boolean dispatchSelected) {
        final ItemInfo curInfo = infoForPosition(item);
        int destX = 0;
        if (curInfo != null) {
            final int width = getClientWidth();
            //Calculating offset
            destX = (int) (width * Math.max(mFirstOffset,
                    Math.min(curInfo.offset, mLastOffset)));
        }
        //If it is smooth sliding
        if (smoothScroll) {
            //Later specific analysis
            smoothScrollTo(destX, 0, velocity);
            //Callback interface
            if (dispatchSelected) {
                dispatchOnPageSelected(item);
            }
        } else {
          //Callback interface
            if (dispatchSelected) {
                dispatchOnPageSelected(item);
            }
            //End sliding directly with scrollTo
            completeScroll(false);
            scrollTo(destX, 0);
            pageScrolled(destX);
        }
    }

smoothScrollTo()

   void smoothScrollTo(int x, int y, int velocity) {
        ...
        //x-Axis Sliding Starting Position
        int sx;
        boolean wasScrolling = (mScroller != null) && !mScroller.isFinished();
        //If you're scrolling at this point
        if (wasScrolling) {
          //Update start location
            sx = mIsScrollStarted ? mScroller.getCurrX() : mScroller.getStartX();
            mScroller.abortAnimation();
            setScrollingCacheEnabled(false);
        } else {
            sx = getScrollX();
        }
        //y-axis sliding starting position
        int sy = getScrollY();

        int dx = x - sx;
        int dy = y - sy;
        if (dx == 0 && dy == 0) {
            completeScroll(false);
            populate();
            setScrollState(SCROLL_STATE_IDLE);
            return;
        }

        setScrollingCacheEnabled(true);
        setScrollState(SCROLL_STATE_SETTLING);

        final int width = getClientWidth();
        final int halfWidth = width / 2;
        //Sliding distance, distance affects time
        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
        final float distance = halfWidth + halfWidth
                * distanceInfluenceForSnapDuration(distanceRatio);
        //Sliding time
        int duration;
        velocity = Math.abs(velocity);
        if (velocity > 0) {
            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
        } else {
            final float pageWidth = width * mAdapter.getPageWidth(mCurItem);
            final float pageDelta = (float) Math.abs(dx) / (pageWidth + mPageMargin);
            duration = (int) ((pageDelta + 1) * 100);
        }
        duration = Math.min(duration, MAX_SETTLE_DURATION);

        // Reset the "scroll started" flag. It will be flipped to true in all places
        // where we call computeScrollOffset().
        mIsScrollStarted = false;

        //The important thing is to use Scroller to generate elastic sliding.
        mScroller.startScroll(sx, sy, dx, dy, duration);
       //Redraw for callback
        ViewCompat.postInvalidateOnAnimation(this);
    }

computeScroll()

From the previous analysis, we know that ViewPager uses Scroller to generate conversational sliding. The key of Scroller to generate elastic sliding is that it calls back computeScroll() in onDraw(), then slides with scrollTo() in this method and applies for redrawing again. ViewPager rewrites this method, and after calling scrollTo(), pageScrolled(x) is also called to update Decor View, call back interface, and generate animation. Then apply for redrawing.

    @Override
    public void computeScroll() {
        mIsScrollStarted = true;
        if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
            int oldX = getScrollX();
            int oldY = getScrollY();
            int x = mScroller.getCurrX();
            int y = mScroller.getCurrY();

            if (oldX != x || oldY != y) {
                scrollTo(x, y);
                if (!pageScrolled(x)) {
                    mScroller.abortAnimation();
                    scrollTo(0, y);
                }
            }

            // Keep on drawing until the animation has finished.
            ViewCompat.postInvalidateOnAnimation(this);
            return;
        }

        // Done with scroll, clean up state.
        completeScroll(true);
    }

Summary

ViewPager's drag and slide are all finished, drag is responded to in onInterceptTouchEvent() and onTouchEvent() Move, scrollTo method is used to form the view movement, and pageScrolled() is used to complete the processing of related things, including Decor View, interface method callback, animation; Up of onTouchEvent() may produce smooth sliding, using initialization time. Scroller defined.

Here we have a general understanding of the mobile process of ViewPager's view, and some details can be quickly positioned. Next, I want to look at the interaction between ViewPager and Fragment.

Keywords: Mobile Fragment

Added by starnol on Wed, 19 Jun 2019 21:00:11 +0300