Libathos
Libathos

Reputation: 3362

How to scroll a Pager programatically AND smoothly

I'm using this code to scroll programatically my pager

public void MoveNext(View view) {
    pager.setCurrentItem(pager.getCurrentItem() + 1);
}

public void MovePrevious(View view) {

    pager.setCurrentItem(pager.getCurrentItem() - 1);

}

The code work perfect but the transition is too fast. How can I introduce a delay so that the scrolling would be done more smoothly?

Upvotes: 2

Views: 4017

Answers (2)

VinceStyling
VinceStyling

Reputation: 3827

I believe there are two ways can achieve this goal, I've tried by ViewPager's fakeDrag() but which doesn't work perfect, luckly, another way does, by simulate touch motion event and use ObjectAnimator to specify the animation duration then make control the scroll speed become true.

public class ViewPagerActivity extends FragmentActivity
        implements View.OnClickListener, Animator.AnimatorListener {

    private ViewPager mViewPager;
    private View btnTriggerNext;
    private View btnTriggerPrev;

    @Override
    protected void onCreate(...) {
        super...;
        setContentView(R.layout.layout_xml);

        mViewPager = findViewById(...);

        btnTriggerNext = findViewById(R.id.btnTriggerNext);
        btnTriggerNext.setOnClickListener(this);

        btnTriggerPrev = findViewById(R.id.btnTriggerPrev);
        btnTriggerPrev.setOnClickListener(this);
    }

    private boolean mIsInAnimation;
    private long mMotionBeginTime;
    private float mLastMotionX;

    @Override
    public void onClick(View v) {
        if (mIsInAnimation) return;
        ObjectAnimator anim;

        if (v == btnTriggerPrev) {
            if (!hasPrevPage()) return;
            anim = ObjectAnimator.ofFloat(this, "motionX", 0, mViewPager.getWidth());
        }
        else if (v == btnTriggerNext) {
            if (!hasNextPage()) return;
            anim = ObjectAnimator.ofFloat(this, "motionX", 0, -mViewPager.getWidth());
        }
        else return;

        anim.setInterpolator(new LinearInterpolator());
        anim.addListener(this);
        anim.setDuration(300);
        anim.start();
    }

    public void setMotionX(float motionX) {
        if (!mIsInAnimation) return;
        mLastMotionX = motionX;
        final long time = SystemClock.uptimeMillis();
        simulate(MotionEvent.ACTION_MOVE, mMotionBeginTime, time);
    }

    @Override
    public void onAnimationEnd(Animator animation) {
        mIsInAnimation = false;
        final long time = SystemClock.uptimeMillis();
        simulate(MotionEvent.ACTION_UP, mMotionBeginTime, time);
    }

    @Override
    public void onAnimationStart(Animator animation) {
        mLastMotionX = 0;
        mIsInAnimation = true;
        final long time = SystemClock.uptimeMillis();
        simulate(MotionEvent.ACTION_DOWN, time, time);
        mMotionBeginTime = time;
    }

    // method from http://stackoverflow.com/a/11599282/1294681
    private void simulate(int action, long startTime, long endTime) {
        // specify the property for the two touch points
        MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[1];
        MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties();
        pp.id = 0;
        pp.toolType = MotionEvent.TOOL_TYPE_FINGER;

        properties[0] = pp;

        // specify the coordinations of the two touch points
        // NOTE: you MUST set the pressure and size value, or it doesn't work
        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1];
        MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords();
        pc.x = mLastMotionX;
        pc.pressure = 1;
        pc.size = 1;
        pointerCoords[0] = pc;

        final MotionEvent ev = MotionEvent.obtain(
                startTime, endTime, action, 1, properties,
                pointerCoords, 0,  0, 1, 1, 0, 0, 0, 0);

        mViewPager.dispatchTouchEvent(ev);
    }

    private boolean hasPrevPage() {
        return mViewPager.getCurrentItem() > 0;
    }

    private boolean hasNextPage() {
        return mViewPager.getCurrentItem() + 1 < mViewPager.getAdapter().getCount();
    }

    @Override
    public void onAnimationCancel(Animator animation) {
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
    }
}

cause it was simulating touch event, so please use a proper duration(less than 600ms will be nice) to do scrolling, when scroll in progress, put down finger would stop it and cause some bugs.

Upvotes: 6

Apoorv
Apoorv

Reputation: 13520

Change

pager.setCurrentItem(pager.getCurrentItem() + 1);

to

pager.setCurrentItem(pager.getCurrentItem() + 1,true);

This will call the method setCurrentItem(int item,boolean smoothScroll) which will scroll smoothly to the mentioned item instead of transitioning immediately

Upvotes: 1

Related Questions