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 && 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:state_selected="true"/> <item android:drawable="@drawable/viewpager_un_selected_dot"/> </selector>
3.2 Resource file for selected dot named is viewpager_selected_dot
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape android:innerRadius="0dp" android:shape="ring" android:thickness="5dp" android:useLevel="false"> <solid android:color="@color/colorAccent"/> </shape> </item> </layer-list>
3.4 Resource file for unselected dot named is viewpager_un_selected_dot
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape android:layout_width="match_parent" android:layout_height="wrap_content" android:innerRadius="0dp" android:shape="ring" android:thickness="5dp" android:useLevel="false"> <solid android:color="@android:color/transparent" /> <stroke android:color="#e0e0e0" android:width="1dp"/> </shape> </item> </layer-list>
I’m using a few other as well. but it’s not important
4. Write FragmentPagerAdapter
Create a subclass of FragmentPagerAdapter named is AutoScrollPagerAdapter and paste below code
package com.example.autoscrollviewpager.ui.main; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; public class AutoScrollPagerAdapter extends FragmentPagerAdapter { public AutoScrollPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { // Return a SlideFragment (defined as a static inner class below). return SlideFragment.newInstance(position + 1); } @Override public int getCount() { // Show 3 total pages. return 3; } }
5. Create a Fragment named is SlideFragment
Above code, you see we are using a common in AutoScrollPagerAdapter. Next step will we create a new fragment with layout. Open the layout file and add below code
<?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:id="@+id/constraintLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.main.SlideFragment" > <TextView android:id="@+id/section_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:layout_marginTop="32dp" android:gravity="center" android:text="Code" android:textAllCaps="true" android:textColor="@color/colorWhite" android:textSize="22sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/imageView" tools:layout_constraintLeft_creator="1" tools:layout_constraintTop_creator="1" /> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:src="@drawable/code_icon" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
In Java file paste this code
your design stuff is almost done and let’s come to the java coding part. In the below code you see, we are using two ids arrays that contain ids of title and images. Later on, you are observing live data change that observing the position of the current fragment. Based on the slide postion we are setting text and images in this fragment.
package com.example.autoscrollviewpager.ui.main; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import com.example.autoscrollviewpager.R; public class SlideFragment extends Fragment { private static final String ARG_SECTION_NUMBER = "section_number"; @StringRes private static final int[] PAGE_TITLES = new int[] { R.string.page_text_1, R.string.page_text_2, R.string.page_text_3 }; @StringRes private static final int[] PAGE_IMAGE = new int[] { R.drawable.code_icon, R.drawable.eat_icon, R.drawable.sleep_icon }; private SliderViewModel sliderViewModel; public static SlideFragment newInstance(int index) { SlideFragment fragment = new SlideFragment(); Bundle bundle = new Bundle(); bundle.putInt(ARG_SECTION_NUMBER, index); fragment.setArguments(bundle); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); sliderViewModel = ViewModelProviders.of(this).get(SliderViewModel.class); int index = 1; if (getArguments() != null) { index = getArguments().getInt(ARG_SECTION_NUMBER); } sliderViewModel.setIndex(index); } @Override public View onCreateView( @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_main, container, false); final TextView textView = root.findViewById(R.id.section_label); final ImageView imageView = root.findViewById(R.id.imageView); sliderViewModel.getText().observe(this, new Observer<Integer>() { @Override public void onChanged(Integer index) { textView.setText(PAGE_TITLES[index]); imageView.setImageResource(PAGE_IMAGE[index]); } }); return root; } }
Google recommend ViewModel for communicating fragment
In this ViewModel, we are maintaining the index of the current fragment with the help of MutableLiveData
package com.example.autoscrollviewpager.ui.main; import androidx.arch.core.util.Function; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; public class SliderViewModel extends ViewModel { private MutableLiveData<Integer> mIndex = new MutableLiveData<>(); private LiveData<Integer> mPagerIndex = Transformations.map(mIndex, new Function<Integer, Integer>() { @Override public Integer apply(Integer input) { return input - 1; } }); public void setIndex(int index) { mIndex.setValue(index); } public LiveData<Integer> getText() { return mPagerIndex; } }
Finally, Open MainActivity and add below line of code
Put the blow closing code in MainActivity. Its code is self-explanatory
package com.example.autoscrollviewpager; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import com.example.autoscrollviewpager.ui.main.AutoScrollPagerAdapter; import com.google.android.material.tabs.TabLayout; public class MainActivity extends AppCompatActivity { private static final int AUTO_SCROLL_THRESHOLD_IN_MILLI = 1000; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); AutoScrollPagerAdapter autoScrollPagerAdapter = new AutoScrollPagerAdapter(getSupportFragmentManager()); AutoScrollViewPager viewPager = findViewById(R.id.view_pager); viewPager.setAdapter(autoScrollPagerAdapter); TabLayout tabs = findViewById(R.id.tabs); tabs.setupWithViewPager(viewPager); // start auto scroll viewPager.startAutoScroll(); // set auto scroll time in mili viewPager.setInterval(AUTO_SCROLL_THRESHOLD_IN_MILLI); // enable recycling using true viewPager.setCycle(true); } }
Conclusion
That all, In this article, we learned Auto Scroll ViewPager in Android, I hope it’s helpful for you, Help me by sharing this post with all your friends who learning android app development.
Get Solution Code
Keep in touch
If you want to keep in touch and get an email when I write new blog posts, follow me on facebook or subscribe us. It only takes about 10 seconds to register.
Still, if you have any queries please put your comment below.
3 Comments
Perfect …. Thanks for your efforts.
Great work but on touch it is not stopping
great work sir.