0

I am using the VelocityViewPager found in this post in order to implement a flingable ViewPager. I am trying to set the view pager to initialize by showing an element in the middle of the VelocityViewPager (the date 1970). So in the onViewCreated() of my fragment I do:

velocityViewPager.setCurrentItem(69);

A user suggested doing the following to fix this issue in the comments of the post:

private void trackMotion(float distX) {
    final int width = getWidth();
    final int widthWithMargin = width + this.getPageMargin();
    float scrollOffset = getScrollX() - (this.getCurrentItem() * widthWithMargin);
    float scrollX = getScrollX() - distX - scrollOffset;
    ........
}

When I tried this, however, it did not work and I would be stuck seeing elements 68, 69, and 70 of the ViewPager because the scrollX would be stuck because the offset would "lock" the scrollX to a range.

The issue I noticed is the scrollX is initialized to 0 when it really should be a value representing position 69. I tried manually setting the scrollX in XML, setting the scrollX in Java code, and doing various offset computations to no avail.

Community
  • 1
  • 1
Nelson Hoang
  • 413
  • 2
  • 11

1 Answers1

1

This is what I ended up doing for a flingable view. It is definitely more painful (it required a lot of trial and error on my part) than just extending from view pager, but it gave me what I wanted. You will have to figure out a way to use the View's scrollX in order to determine what item to snap to and what to do when you reach the end or start of the view.

public class FlingableScroller extends View implements GestureDetector.OnGestureListener {

    public FlingableScroller(Context context, AttributeSet attrs) {
        super(context, attrs);
        mGestureDetector = new GestureDetector(context, this);
        mScroller = new OverScroller(context);
        //This is how you "set current item" You will have to calculate INITIAL_SCROLL_X yourself
        setScrollX(INITIAL_SCROLL_X);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        animateFlingIfApplicable();
    }

    private void animateFlingIfApplicable() {
        //We have a fling only if computeScrollOffset is true.
        if (mScroller.computeScrollOffset()) {
            //Flinging passed the start point so continue the fling at the end
            if (mScroller.getFinalX() == 0) {
                int velocity = getFlingVelocity();
                mScroller.forceFinished(true);
                mScroller.fling(MAXIMUM_SCROLL_X, 0, velocity / FRICTION_COEFFICIENT, 0, 0, MAXIMUM_SCROLL_X, 0, 0);
                //Flinging passed the end point so continue the fling at the start
            } else if (mScroller.getFinalX() == MAXIMUM_SCROLL_X) {
                int velocity = getFlingVelocity();
                mScroller.forceFinished(true);
                mScroller.fling(0, 0, velocity / FRICTION_COEFFICIENT, 0, 0, MAXIMUM_SCROLL_X, 0, 0);
            } else if (mScroller.getFinalX() == getScrollX() || mScroller.getCurrVelocity() == 0) {
                snapToItem();
                mScroller.forceFinished(true);
            } else {
                scrollTo(mScroller.getCurrX(), 0);
            }
        }

        invalidate();
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        //This logic handles the case when you scroll pass the beginning or the end of the scroller
        if (getScrollX() > MAXIMUM_SCROLL_X) {
            scrollTo(0, 0);
        } else if (getScrollX() >= 0) {
            scrollBy(distance, 0);
        } else {
            scrollTo(MAXIMUM_SCROLL_X, 0);
        }
        invalidate();
        return true;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        //This is to prevent low velocity "flings"
        if (Math.abs(velocityX) < 400 * SCREEN_DENSITY) {
            return false;
        }

        mScroller.forceFinished(true);
        //Define friction_coefficient to a value that gives you desirable flinging.
        mScroller.fling(getScrollX(), getScrollY(), (int) -velocityX / FRICTION_COEFFICIENT, 0, 0, MAXIMUM_SCROLL_X, 0, 0);
        invalidate();
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mScroller.forceFinished(true);
                break;
            case MotionEvent.ACTION_UP:
                snapToItem();
                invalidate();
                break;
    }
        return mGestureDetector.onTouchEvent(event);
    }

    private void snapToItem() {
        //The the user lifts up their finger from a scroll or when a fling finishes determine what item to snap to. See the ViewPager source code to emulate the "fake drag"
        int scrollByValue = getScrollX() / SOME_VALUE
        scrollTo(scrollByValue, 0);
    }
}
Nelson Hoang
  • 413
  • 2
  • 11