19

I have a ViewPager that I want to rotate automatically every 5 seconds, whilst also allowing the user to swipe too. However, when I set the automatic change, the transition animation between pages happens really quickly, and I want this to be slower.

I've seen the answers for this question here:

Slowing speed of Viewpager controller in android

...but they all use reflection. Does anyone know of any way of slowing down the automatic speed of a ViewPager without using reflection?

I thought of using a PageTransformer, but not sure if that would work, and also it would probably affect the speed if the user swiped as well as the automatic one? Unless I can somehow detect the difference, and then do one or another PageTransformation?

Lii
  • 11,553
  • 8
  • 64
  • 88
Russ Wheeler
  • 2,590
  • 5
  • 30
  • 57

3 Answers3

41

Use this Custom View

public class ViewPagerCustomDuration extends ViewPager {
    private FixedSpeedScroller mScroller = null;

    public ViewPagerCustomDuration(Context context) {
        super(context);
        init();
    }

    public ViewPagerCustomDuration(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /*
     * Override the Scroller instance with our own class so we can change the
     * duration
     */
    private void init() {
        try {
            Class<?> viewpager = ViewPager.class;
            Field scroller = viewpager.getDeclaredField("mScroller");
            scroller.setAccessible(true);
            mScroller = new FixedSpeedScroller(getContext(),
                    new DecelerateInterpolator());
            scroller.set(this, mScroller);
        } catch (Exception ignored) {
        }
    }

    /*
     * Set the factor by which the duration will change
     */
    public void setScrollDuration(int duration) {
        mScroller.setScrollDuration(duration);
    }

    private class FixedSpeedScroller extends Scroller {

        private int mDuration = 500;

        public FixedSpeedScroller(Context context) {
            super(context);
        }

        public FixedSpeedScroller(Context context, Interpolator interpolator) {
            super(context, interpolator);
        }

        public FixedSpeedScroller(Context context, Interpolator interpolator, boolean flywheel) {
            super(context, interpolator, flywheel);
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy, int duration) {
            // Ignore received duration, use fixed one instead
            super.startScroll(startX, startY, dx, dy, mDuration);
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy) {
            // Ignore received duration, use fixed one instead
            super.startScroll(startX, startY, dx, dy, mDuration);
        }

        public void setScrollDuration(int duration) {
            mDuration = duration;
        }
    }
}
Lii
  • 11,553
  • 8
  • 64
  • 88
Alexandr Larin
  • 793
  • 7
  • 13
  • 3
    I don't actually remember why I asked this or how I solved it, but this answer has enough up ticks, that I'm willing to accept it as a correct answer. Also, @Ezio seems to think it works :D – Russ Wheeler Oct 09 '17 at 11:56
  • Excellent solution. – marius Jun 15 '18 at 17:59
  • 5
    Why wouldn't it be adjustable at least for component in Compat library? It's 2018, I think we deserve to have ability to customize animation speed by now... – Den Drobiazko Oct 16 '18 at 12:11
  • @DenRimus So true.. unbelievable. – idish Nov 17 '18 at 22:56
  • **Note that this class uses reflection to access an internal fields of `ViewPager`. Hence it might stop working at any Android update!** – Lii Nov 08 '19 at 09:11
  • 3
    @RussWheeler: In your question text you explicitly ask for a solution **without reflection**, and this solution uses reflection to access a private field. Worse yet, the poster doesn't even bother to point that out. I don't think you should accept this answer. The question is still valid and unanswered. – Lii Nov 08 '19 at 09:13
  • god this is dirty – John Sardinha Sep 13 '21 at 16:36
6

You create custom view pager

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.Scroller;

import java.lang.reflect.Field;

public class CustomViewPager extends ViewPager {

    private FixedSpeedScroller mScroller = null;
    private boolean enabled;

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.enabled = true;
        init();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (this.enabled) {
            return super.onTouchEvent(event);
        }
        init();
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (this.enabled) {
            return super.onInterceptTouchEvent(event);
        }
        return false;
    }

    public void setPagingEnabled(boolean enabled) {
        this.enabled = enabled;
    }


    /*
     * Override the Scroller instance with our own class so we can change the
     * duration
     */
    private void init() {
        try {
            Class<?> viewpager = ViewPager.class;
            Field scroller = viewpager.getDeclaredField("mScroller");
            scroller.setAccessible(true);
            mScroller = new FixedSpeedScroller(getContext(),
                    new DecelerateInterpolator());
            scroller.set(this, mScroller);
        } catch (Exception ignored) {
        }
    }

    /*
     * Set the factor by which the duration will change
     */
    public void setScrollDuration(int duration) {
        mScroller.setScrollDuration(duration);
    }

    private class FixedSpeedScroller extends Scroller {

        private int mDuration = 500;

        public FixedSpeedScroller(Context context) {
            super(context);
        }

        public FixedSpeedScroller(Context context, Interpolator interpolator) {
            super(context, interpolator);
        }

        public FixedSpeedScroller(Context context, Interpolator interpolator, boolean flywheel) {
            super(context, interpolator, flywheel);
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy, int duration) {
            // Ignore received duration, use fixed one instead
            super.startScroll(startX, startY, dx, dy, mDuration);
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy) {
            // Ignore received duration, use fixed one instead
            super.startScroll(startX, startY, dx, dy, mDuration);
        }

        public void setScrollDuration(int duration) {
            mDuration = duration;
        }
    }

}

you can set duration of view pager according to you

Prabh deep
  • 1,024
  • 11
  • 15
1

use schedule timer option for auto rotate the pages.

int i = 0;
static Timer timer = new Timer();

//Scroller scroll = new Scroller(this);

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.simple_circles);

    mAdapter = new TestFragmentAdapter(getSupportFragmentManager());

    mPager = (ViewPager) findViewById(R.id.pager);

    mPager.setAdapter(mAdapter);

     mIndicator = (CirclePageIndicator) findViewById(R.id.indicator);

    mIndicator.setViewPager(mPager);

    mPager.setCurrentItem(i);

    SwitchPage(3);


  }

public void SwitchPage(int seconds) 

{

if(timer != null)
{
    timer.cancel();
}

timer = new Timer(); // At this line a new Thread will be created

timer.schedule(new SwitchPageTask(), 
2000, seconds * 2000);               
 // delay in milliseconds
 }


class SwitchPageTask extends TimerTask 
{

@Override
public void run() {

    runOnUiThread(new Runnable() {

        public void run() {

            if(i < mAdapter.getCount())

            {

                i++;

                mPager.setCurrentItem(i, true);

            }
            else
            {

                i=0;

                mPager.setCurrentItem(i, true);
            }
        }
    });
}
}

just cancel the timer like.. timer.cancel().. or
cancel timer from another activity or fragment activity..

    textView1_page3.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

                if (SampleCirclesDefault.timer != null) {
                    SampleCirclesDefault.timer.cancel();
                    Toast.makeText(getActivity(), "timer
                  cancled", 1).show();
                }

            }
        });
harikrishnan
  • 1,985
  • 4
  • 32
  • 63
  • harikrishnan looking through your code it looks like your changing the delay in between the change. That's not what I have a problem with. I think the actual animation of the auto transition is too quick, it's almost instant, I want it to scroll from one image to the next slower, not at a variable time difference. I'll update my original question to make that clearer. – Russ Wheeler Jan 30 '15 at 12:23