18

Is there any way to make the support package ViewPager snap to the next page with a shorter drag? The default behaviour seems to be that even if I drag almost 75% the page still snaps back to the previous page when I let go. I'd like to make the snap threshold shorter and make the ViewPager snap to the next page instead.

Note that this applied to drag gesture. A fling gesture requires much shorter gesture already.

Juhani
  • 5,076
  • 5
  • 33
  • 35

4 Answers4

24

You can do this ad-hoc, without worrying too much about the internals of ViewPager as long as you want to increase the target zone:

private class MyPageChangeListener implements OnPageChangeListener {
    private float mLastPositionOffset = 0f;
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if(positionOffset < mLastPositionOffset && positionOffset < 0.9) {
            mViewPager.setCurrentItem(position);
        } else if(positionOffset > mLastPositionOffset && positionOffset > 0.1) {
            mViewPager.setCurrentItem(position+1);
        }
        mLastPositionOffset = positionOffset;
    }
}
straya
  • 5,002
  • 1
  • 28
  • 35
1

Looks like these values are hard-coded in a private method, so there's no simple way to override them.

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.2.2_r1/android/support/v4/view/ViewPager.java#2075

astuetz
  • 2,693
  • 2
  • 23
  • 22
  • 3
    Correct, but the values aren't 0.4/0.6f (as they're truncators). I *think* it works out to be 60% of the ViewPager width for both directions. A fling only needs to be 25 dips, with a 400 dip/s velocity. – Chris Banes Jun 26 '13 at 13:41
  • @ChrisBanes Of course you're right, I had just a very quick look at it - thanks! – astuetz Jun 26 '13 at 13:45
1

Here is my code used in Librera Reader

public class MyViewPager extends ViewPager {

  public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        initMyScroller();
    }

    private void initMyScroller() {
        try {
            Class<?> viewpager = ViewPager.class;
            Field scroller = viewpager.getDeclaredField("mScroller");
            scroller.setAccessible(true);
            scroller.set(this, new MyScroller(getContext())); // my liner scroller

            Field mFlingDistance = viewpager.getDeclaredField("mFlingDistance");
            mFlingDistance.setAccessible(true);
            mFlingDistance.set(this, Dips.DP_10);//10 dip short drag

            Field mMinimumVelocity = viewpager.getDeclaredField("mMinimumVelocity");
            mMinimumVelocity.setAccessible(true);
            mMinimumVelocity.set(this, 0); //0 velocity

        } catch (Exception e) {
            LOG.e(e);
        }

    }

    public class MyScroller extends Scroller {
        public MyScroller(Context context) {
            super(context, new LinearInterpolator()); // my LinearInterpolator
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy, int duration) {
            super.startScroll(startX, startY, dx, dy, 175);//175 duration
        }
    }

 }
Foobnix
  • 719
  • 5
  • 7
  • Please don't post identical answers to multiple questions. Post one good answer, then vote/flag to close the other questions as duplicates. If the question is not a duplicate, *tailor your answers to the question.* – Paul Roub Mar 12 '18 at 13:26
1

I did this using reflection. By setting a private field mMinimumVelocity to -1, you make the ViewPager take all your scrolls as a fling scroll - which needs much shorter drag to switch to the next page. Works like a charm!

Field mMinimumVelocity;
mMinimumVelocity = ViewPager.class.getDeclaredField("mMinimumVelocity");
mMinimumVelocity.setAccessible(true);
mMinimumVelocity.set(mPager, -1);

The source code from which I know the private fields is here.

Also if you want to make the threshold a little bigger than the default 25dp of the fling scroll, you can set the mFlingDistance field to some higher value, e.g. 45dp. But remember it also affects the required drag distance of the actual fling scroll.

Field mFlingDistance;
mFlingDistance= ViewPager.class.getDeclaredField("mFlingDistance");
mFlingDistance.setAccessible(true);
mFlingDistance.set(mPager, (int) (45 * context.getResources().getDisplayMetrics().density));
David Riha
  • 1,368
  • 14
  • 29