UI/UX

Auto Scroll ViewPager Android

Welcome Guys, Recently I have implemented Auto Scroll ViewPager. It’s not easy, that why I have simplified it in step. In this android tutorial, I will tell you the real implementation of auto scroll ViewPager Android with proper step. I have created a sample application that contains ViewPager, FragmentStatePagerAdapter, and TabLayout. It’s very easy, just follow below steps.

Auto Scroll ViewPager Demo App

Prerequisite

In this demo app, we’ll use auto scrollViewPager with tab indicator. For auto scroll ViewPager implementation, you have to basic knowledge of ViewPager and TabLayout. If you have then it’s good, 😉 otherwise, read our another article there I have explained all core concepts of ViewPager in Android.

Implementation steps of Auto Scroll ViewPager

  • Project Setup
  • Create a custom component (AutoScrollViewPager)
  • Prepare XML layout
  • Write FragmentPagerAdapter
  • Create an Adapter item Fragment
    • Design layout for slider item
    • Bind this layout to fragment
  • Create a ViewModel for ViewPager communication
  • Finally, update Activity class
    • Create an instance of FragmentPagerAdapter
    • Set this adapter on ViewPager
    • Set ViewPager on tab layout for tab indicator

1. Project Setup

Let’s move to Android Studio, create a new project with androidx and material io library. after doing that your app build.gradle seems like below.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'com.google.android.material:material:1.0.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

2.1 Create a custom component (AutoScrollViewPager)

In source, folder creates a subclass of ViewPager named is AutoScrollViewPager and paste below code.

package com.example.autoscrollviewpager;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.animation.Interpolator;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;

public class AutoScrollViewPager extends ViewPager {

  public static final int DEFAULT_INTERVAL = 1500;

  public static final int LEFT = 0;
  public static final int RIGHT = 1;

  private static final String TAG = "AutoScrollViewPager";
  public static final int SLIDE_BORDER_MODE_NONE = 0;
  public static final int SLIDE_BORDER_MODE_CYCLE = 1;
  public static final int SLIDE_BORDER_MODE_TO_PARENT = 2;

  private long interval = DEFAULT_INTERVAL;
  private int direction = RIGHT;
  private boolean isCycle = true;
  private boolean stopScrollWhenTouch = true;
  private int slideBorderMode = SLIDE_BORDER_MODE_NONE;
  private boolean isBorderAnimation = true;
  private double autoScrollFactor = 1.0;
  private double swipeScrollFactor = 1.0;

  private Handler handler;
  @Nullable
  private DurationScroller scroller;

  public static final int SCROLL_WHAT = 0;

  public AutoScrollViewPager(Context paramContext) {
    super(paramContext);
    init();
  }

  public AutoScrollViewPager(Context paramContext, AttributeSet paramAttributeSet) {
    super(paramContext, paramAttributeSet);
    init();
  }

  private void init() {
    handler = new MyHandler(this);
    setViewPagerScroller();
  }

  /**
   * start auto scroll, first scroll delay time is {@link #getInterval()}.
   */
  public void startAutoScroll() {
    if (scroller != null) {
      sendScrollMessage(
          (long) (interval + scroller.getDuration() / autoScrollFactor * swipeScrollFactor));
    }
  }

  /**
   * start auto scroll.
   *
   * @param delayTimeInMills first scroll delay time.
   */
  public void startAutoScroll(int delayTimeInMills) {
    sendScrollMessage(delayTimeInMills);
  }

  /**
   * stop auto scroll.
   */
  public void stopAutoScroll() {
    handler.removeMessages(SCROLL_WHAT);
  }

  /**
   * set the factor by which the duration of sliding animation will change while swiping.
   */
  public void setSwipeScrollDurationFactor(double scrollFactor) {
    swipeScrollFactor = scrollFactor;
  }

  /**
   * set the factor by which the duration of sliding animation will change while auto scrolling.
   */
  public void setAutoScrollDurationFactor(double scrollFactor) {
    autoScrollFactor = scrollFactor;
  }

  private void sendScrollMessage(long delayTimeInMills) {
    /** remove messages before, keeps one message is running at most **/
    handler.removeMessages(SCROLL_WHAT);
    handler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills);
  }

  /**
   * set ViewPager scroller to change animation duration when sliding.
   */
  private void setViewPagerScroller() {
    try {
      Field scrollerField = ViewPager.class.getDeclaredField("mScroller");
      scrollerField.setAccessible(true);
      Field interpolatorField = ViewPager.class.getDeclaredField("sInterpolator");
      interpolatorField.setAccessible(true);

      scroller =
          new DurationScroller(getContext(), (Interpolator) interpolatorField.get(null));
      scrollerField.set(this, scroller);
    } catch (IllegalAccessException e) {
      Log.e(TAG, "setViewPagerScroller: ",e );
    //  Timber.e(e);
    } catch (NoSuchFieldException e) {
      Log.e(TAG, "setViewPagerScroller: ",e );
     // Timber.e(e);
    }
  }

  /**
   * scroll only once.
   */
  public void scrollOnce() {
    PagerAdapter adapter = getAdapter();
    int currentItem = getCurrentItem();
    int totalCount = adapter != null ? adapter.getCount() : -100;
    if (adapter == null || totalCount <= 1) {
      return;
    }

    int nextItem = (direction == LEFT) ? --currentItem : ++currentItem;
    if (nextItem < 0) {
      if (isCycle) {
        setCurrentItem(totalCount - 1, isBorderAnimation);
      }
    } else if (nextItem == totalCount) {
      if (isCycle) {
        setCurrentItem(0, isBorderAnimation);
      }
    } else {
      setCurrentItem(nextItem, true);
    }
  }

  private static class MyHandler extends Handler {

    private final WeakReference<AutoScrollViewPager> autoScrollViewPager;

    public MyHandler(AutoScrollViewPager autoScrollViewPager) {
      this.autoScrollViewPager = new WeakReference<AutoScrollViewPager>(autoScrollViewPager);
    }

    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);

      if (msg.what == SCROLL_WHAT) {
        AutoScrollViewPager pager = this.autoScrollViewPager.get();
        if (pager != null &amp;&amp; pager.scroller != null) {
          pager.scroller.setScrollDurationFactor(pager.autoScrollFactor);
          pager.scrollOnce();
          pager.scroller.setScrollDurationFactor(pager.swipeScrollFactor);
          pager.sendScrollMessage(pager.interval + pager.scroller.getDuration());
        }
      }
    }
  }

  /**
   * get auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL}.
   *
   * @return the interval.
   */
  public long getInterval() {
    return interval;
  }

  /**
   * set auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL}.
   *
   * @param interval the interval to set.
   */
  public void setInterval(long interval) {
    this.interval = interval;
  }

  /**
   * get auto scroll direction.
   *
   * @return {@link #LEFT} or {@link #RIGHT}, default is {@link #RIGHT}
   */
  public int getDirection() {
    return (direction == LEFT) ? LEFT : RIGHT;
  }

  /**
   * set auto scroll direction.
   *
   * @param direction {@link #LEFT} or {@link #RIGHT}, default is {@link #RIGHT}
   */
  public void setDirection(int direction) {
    this.direction = direction;
  }

  /**
   * whether automatic cycle when auto scroll reaching the last or first item, default is true.
   *
   * @return the isCycle.
   */
  public boolean isCycleScroll() {
    return isCycle;
  }

  /**
   * set whether automatic cycle when auto scroll reaching the last or first item, default is true.
   *
   * @param isCycle the isCycle to set.
   */
  public void setCycle(boolean isCycle) {
    this.isCycle = isCycle;
  }

  /**
   * whether stop auto scroll when touching, default is true.
   *
   * @return the stopScrollWhenTouch.
   */
  public boolean isStopScrollWhenTouch() {
    return stopScrollWhenTouch;
  }

  /**
   * set whether stop auto scroll when touching, default is true.
   */
  public void setStopScrollWhenTouch(boolean stopScrollWhenTouch) {
    this.stopScrollWhenTouch = stopScrollWhenTouch;
  }

  /**
   * get how to process when sliding at the last or first item.
   *
   * @return the slideBorderMode {@link #SLIDE_BORDER_MODE_NONE},
   * {@link #SLIDE_BORDER_MODE_TO_PARENT},
   * {@link #SLIDE_BORDER_MODE_CYCLE}, default is {@link #SLIDE_BORDER_MODE_NONE}
   */
  public int getSlideBorderMode() {
    return slideBorderMode;
  }

  /**
   * set how to process when sliding at the last or first item.
   *
   * @param slideBorderMode {@link #SLIDE_BORDER_MODE_NONE}, {@link #SLIDE_BORDER_MODE_TO_PARENT},
   * {@link #SLIDE_BORDER_MODE_CYCLE}, default is {@link #SLIDE_BORDER_MODE_NONE}
   */
  public void setSlideBorderMode(int slideBorderMode) {
    this.slideBorderMode = slideBorderMode;
  }

  /**
   * whether animating when auto scroll at the last or first item, default is true.
   */
  public boolean isBorderAnimationEnabled() {
    return isBorderAnimation;
  }

  /**
   * set whether animating when auto scroll at the last or first item, default is true.
   */
  public void setBorderAnimation(boolean isBorderAnimation) {
    this.isBorderAnimation = isBorderAnimation;
  }
}

2.2 Creates a custom DurationScroller

For controlling the scroll duration, create a java class named is DurationScroller and paste below code.

package com.example.autoscrollviewpager;

import android.content.Context;
import android.view.animation.Interpolator;
import android.widget.Scroller;

public class DurationScroller extends Scroller {

  private double scrollFactor = 1;

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

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

  public void setScrollDurationFactor(double scrollFactor) {
    this.scrollFactor = scrollFactor;
  }

  @Override
  public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    super.startScroll(startX, startY, dx, dy, (int)(duration * scrollFactor));
  }
}
Summary uses AutoScrollViewPager

Now your components are ready to use. For using this replace your ViewPager component with this.

 <android.support.v4.view.ViewPager
      android:id="@+id/view_pager"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      />

Replace with

  <com.example.autoscrollviewpager.AutoScrollViewPager
      android:id="@+id/view_pager"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      />

And we can implement followings settings.

  • startAutoScroll()  – start auto scroll
  • startAutoScroll(int) –  start auto scroll delayed.
  • stopAutoScroll()  – stop auto scroll.
  • setInterval(long) –  set auto scroll time in milliseconds, default is DEFAULT_INTERVAL.
  • setDirection(int) –  set auto scroll direction, default is RIGHT.
  • setCycle(boolean) –  set whether automatic cycle when auto scroll reaching the last

That all about setting now comes to implematation

3. Prepare XML layout

Open main activity xml file and add below code. In this code I’m adding AutoScrollViewPager, TabLayout for tab indicator, and some view for beautying the layout.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/background"
    tools:context=".MainActivity"
    >
  <LinearLayout
      android:id="@+id/layoutSignUp"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_margin="@dimen/default_margin"
      android:gravity="center"
      android:orientation="horizontal"
      app:layout_constraintBottom_toBottomOf="parent"
      >

    <TextView
        android:id="@+id/textViewFreeAccount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/free_account"
        android:textColor="@color/colorTextLight"
        android:textSize="@dimen/text_12"
        />

    <TextView
        android:id="@+id/tvBecomeMember"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/default_margin"
        android:layout_marginStart="@dimen/default_margin"
        android:text="@string/become_member_text"
        android:textColor="@color/colorAccent"
        android:textSize="@dimen/text_12"
        android:textStyle="bold"
        />

  </LinearLayout>

  <View
      android:id="@+id/view"
      android:layout_width="match_parent"
      android:layout_height="2dp"
      android:layout_marginBottom="@dimen/default_margin"
      android:background="@drawable/divider_dotted"
      android:layerType="software"
      app:layout_constraintBottom_toTopOf="@id/layoutSignUp"
      />

  <androidx.appcompat.widget.AppCompatTextView
      android:id="@+id/tvTermCondition"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginBottom="@dimen/dp_32"
      android:letterSpacing="0.05"
      android:text="@string/terms_conditions"
      android:textColor="@color/colorWhite"
      android:textSize="@dimen/text_10"
      app:layout_constraintBottom_toTopOf="@+id/view"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      tools:ignore="UnusedAttribute"
      />
  <androidx.appcompat.widget.AppCompatTextView
      android:id="@+id/login_button"
      android:layout_width="wrap_content"
      android:layout_height="@dimen/dp_36"
      android:layout_margin="@dimen/default_margin"
      android:gravity="center"
      android:paddingEnd="@dimen/max_padding"
      android:paddingLeft="@dimen/max_padding"
      android:paddingRight="@dimen/max_padding"
      android:paddingStart="@dimen/max_padding"
      android:text="@string/on_boarding_login_button_text"
      app:layout_constraintBottom_toTopOf="@+id/tvTermCondition"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      style="@style/app_buttons_style"
      />
  <TextView
      android:id="@+id/textView5"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginBottom="@dimen/default_margin"
      android:text="@string/above_login_button_text"
      android:textColor="@color/colorWhite"
      android:textSize="@dimen/text_12"
      app:layout_constraintBottom_toTopOf="@+id/login_button"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      />

  <com.google.android.material.tabs.TabLayout
      android:id="@+id/tabs"
      android:layout_width="match_parent"
      android:layout_height="@dimen/activity_vertical_margin"
      android:layout_marginBottom="@dimen/activity_vertical_margin"
      app:layout_constraintBottom_toTopOf="@+id/textView5"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@id/view_pager"
      app:tabBackground="@drawable/viewpager_tab_selector"
      app:tabGravity="center"
      app:tabIndicatorHeight="0dp"
      />

  <com.example.autoscrollviewpager.AutoScrollViewPager
      android:id="@+id/view_pager"
      android:layout_width="match_parent"
      android:layout_height="0dp"
      app:layout_constraintBottom_toTopOf="@+id/tabs"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      />
</androidx.constraintlayout.widget.ConstraintLayout>

In this layout, I’m using some resource file these are below

3.1 Create a resource file named is viewpager_tab_selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

  <item android:drawable="@drawable/viewpager_selected_dot"
      android:s