Jon Willis has posted on how to enable an infinite scrolling with his code. In there he said that he made some changes in the ViewPager class int the android support library. Which changes have been made and how is it possible to "recompile" the library with the ViewPager change?
-
Possible duplicate of [ViewPager as a circular queue / wrapping](https://stackoverflow.com/questions/7546224/viewpager-as-a-circular-queue-wrapping) – jzeferino Jul 12 '19 at 10:04
10 Answers
I solved this problem very simply using a little hack in the adapter. Here is my code:
public class MyPagerAdapter extends FragmentStatePagerAdapter
{
public static int LOOPS_COUNT = 1000;
private ArrayList<Product> mProducts;
public MyPagerAdapter(FragmentManager manager, ArrayList<Product> products)
{
super(manager);
mProducts = products;
}
@Override
public Fragment getItem(int position)
{
if (mProducts != null && mProducts.size() > 0)
{
position = position % mProducts.size(); // use modulo for infinite cycling
return MyFragment.newInstance(mProducts.get(position));
}
else
{
return MyFragment.newInstance(null);
}
}
@Override
public int getCount()
{
if (mProducts != null && mProducts.size() > 0)
{
return mProducts.size()*LOOPS_COUNT; // simulate infinite by big number of products
}
else
{
return 1;
}
}
}
And then, in the ViewPager, we set current page to the middle:
mAdapter = new MyPagerAdapter(getSupportFragmentManager(), mProducts);
mViewPager.setAdapter(mAdapter);
mViewPager.setCurrentItem(mViewPager.getChildCount() * MyPagerAdapter.LOOPS_COUNT / 2, false); // set current item in the adapter to middle
![](../../users/profiles/686540.webp)
- 7,143
- 3
- 46
- 61
-
-
6This is a nice and simple solution but performance seems to degrade as you increase the value of `LOOPS_COUNT`. I had to set mine around 100 – howettl May 11 '12 at 16:58
-
mProducts is a list of your data. MyFragment is a fragment which will be displayed in the pager. In my example I have a list of products. Each product has an image and some other parameters. Image is loaded from URL in MyFragment. – petrnohejl Sep 30 '13 at 16:06
-
`mViewPager.getChildCount()` returns 0. should be replaced with `mAdapter.getCount()` – levengli Oct 10 '16 at 09:31
-
3The whole point of using a ViewPager is that is manages the fragments for you. With this solution, it will create a new fragment every time. So yeah, this solution will have a great impact on performance. You should probably rip code from the FragmentStatePagerAdapter and do the modulo trick in here: `if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { return f; } }` – clauziere Nov 09 '16 at 20:26
-
the solution seemed to work, but later i found that performance was reduced so much making app unusable at some point. solution might work if you have few pages. – Usama Saeed US Feb 08 '19 at 19:42
-
if an `Tab/indicator` is attached with the `viewpager` then it will add `mProducts.size()*LOOPS_COUN` tabs. – Muhammad Saqib Jan 14 '20 at 07:17
Thank you for your answer Shereef.
I solved it a little bit differently.
I changed the code of the ViewPager class of the android support library. The method setCurrentItem(int)
changes the page with animation. This method calls an internal method that requires the index and a flag enabling smooth scrolling. This flag is boolean smoothScroll
.
Extending this method with a second parameter boolean smoothScroll
solved it for me.
Calling this method setCurrentItem(int index, boolean smoothScroll)
allowed me to make it scroll indefinitely.
Here is a full example:
Please consider that only the center page is shown. Moreover did I store the pages seperately, allowing me to handle them with more ease.
private class Page {
View page;
List<..> data;
}
// page for predecessor, current, and successor
Page[] pages = new Page[3];
mDayPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
if (mFocusedPage == 0) {
// move some stuff from the
// center to the right here
moveStuff(pages[1], pages[2]);
// move stuff from the left to the center
moveStuff(pages[0], pages[1]);
// retrieve new stuff and insert it to the left page
insertStuff(pages[0]);
}
else if (mFocusedPage == 2) {
// move stuff from the center to the left page
moveStuff(pages[1], pages[0]);
// move stuff from the right to the center page
moveStuff(pages[2], pages[1]);
// retrieve stuff and insert it to the right page
insertStuff(pages[2]);
}
// go back to the center allowing to scroll indefinitely
mDayPager.setCurrentItem(1, false);
}
}
});
However, without Jon Willis Code I wouldn't have solved it myself.
EDIT: here is a blogpost about this:
-
6
-
1I will blog about this ASAP. I will notify you guys about this as a comment here. – thehayro Nov 12 '12 at 16:08
-
-
Yes, here: http://thehayro.blogspot.de/2013/09/infiniteviewpager-infinite-paging.html it was actually in the bottom of my post. – thehayro Jul 21 '15 at 23:51
-
If I scroll fast, then my app is blocked. A seconds after it unblocks. How can I solve this? – Antonio Oct 30 '15 at 03:10
-
-
Hi @AlexS, If I understood your question correctly, you can access the Page's data through `InfinitePagerAdapter.getCurrentIndicator()`. This will return you the data that is currently displayed on the page. – thehayro Apr 07 '20 at 12:08
-
The latest version of the android support library (and androidx) has the mentioned overloaded method already implemented so no android source code modifications are required – Vova Apr 10 '21 at 15:54
Infinite view pager by overriding 4 adapter methods in your existing adapter class
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public CharSequence getPageTitle(int position) {
String title = mTitleList.get(position % mActualTitleListSize);
return title;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
int virtualPosition = position % mActualTitleListSize;
return super.instantiateItem(container, virtualPosition);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
int virtualPosition = position % mActualTitleListSize;
super.destroyItem(container, virtualPosition, object);
}
![](../../users/profiles/648030.webp)
- 14,070
- 1
- 46
- 48
-
1This doesn't work when going backwards (left (passed 0) into the negatives) – MobileMon May 29 '15 at 17:15
-
1
-
2After setting an adapter just add yourAwesomePager.setCurrentItem(Integer.MAX_VALUE / 2); to start from the middle – Androider Feb 08 '16 at 17:44
-
also I would recomment using @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } – Androider Feb 08 '16 at 17:45
-
3Unfortunately , one will get performance problems when using MAX_VALUE for viewpager in combination with setCurrentItem, this is due to the implementation of the viewpager, which loops through all items. you will get ANR / UI-thread block. – havchr Sep 14 '16 at 09:40
-
@havchr - no it won't, we are using same fragments again & again. MaxValue will just allows user to swipe that much of time – Pawan Maheshwari Sep 15 '16 at 08:38
-
1create a simple ViewPager that has infinite views and then try to setCurrentItem backwards more than two steps. It may be that you need pageWidth to be small to allow for multipe visible entries in order to trigger the "bug" I am talking about, but the bug is there. See : http://stackoverflow.com/questions/18740916/viewpager-setcurrentitem-freezes-ui-thread – havchr Sep 15 '16 at 15:22
-
@havchr is right, I'm running into the UI thread blocking right now when calling setCurrentItem. Will have to find another approach without the MAX_VALUE count. – Justin Nov 09 '18 at 16:37
-
@havchr, you have to use FragmentStatePagerAdapter instead of FragmentPagerAdapter because FragmentStatePagerAdapter call onDestroy when the Fragment is overcome offscreenPageLimit while FragmentPagerAdapter not – Jetwiz Feb 09 '20 at 14:07
Actually, I've been looking at the various ways to do this "infinite" pagination, and even though the human notion of time is that it is infinite (even though we have a notion of the beginning and end of time), computers deal in the discrete. There is a minimum and maximum time (that can be adjusted as time goes on, remember the basis of the Y2K scare?).
Anyways, the point of this discussion is that it is/should be sufficient to support a relatively infinite date range through an actually finite date range. A great example of this is the Android framework's CalendarView
implementation, and the WeeksAdapter
within it. The default minimum date is in 1900 and the default maximum date is in 2100, this should cover 99% of the calendar use of anyone within a 10 year radius around today easily.
What they do in their implementation (focused on weeks) is compute the number of weeks between the minimum and maximum date. This becomes the number of pages in the pager. Remember that the pager doesn't need to maintain all of these pages simultaneously (setOffscreenPageLimit(int)
), it just needs to be able to create the page based on the page number (or index/position). In this case the index is the number of weeks that the week is from the minimum date. With this approach you just have to maintain the minimum date and the number of pages (distance to the maximum date), then for any page you can easily compute the week associated with that page. No dancing around the fact that ViewPager
doesn't support looping (a.k.a infinite pagination), and trying to force it to behave like it can scroll infinitely.
new FragmentStatePagerAdapter(getFragmentManager()) {
@Override
public Fragment getItem(int index) {
final Bundle arguments = new Bundle(getArguments());
final Calendar temp_calendar = Calendar.getInstance();
temp_calendar.setTimeInMillis(_minimum_date.getTimeInMillis());
temp_calendar.setFirstDayOfWeek(_calendar.getStartOfWeek());
temp_calendar.add(Calendar.WEEK_OF_YEAR, index);
// Moves to the first day of this week
temp_calendar.add(Calendar.DAY_OF_YEAR,
-UiUtils.modulus(temp_calendar.get(Calendar.DAY_OF_WEEK) - temp_calendar.getFirstDayOfWeek(),
7));
arguments.putLong(KEY_DATE, temp_calendar.getTimeInMillis());
return Fragment.instantiate(getActivity(), WeekDaysFragment.class.getName(), arguments);
}
@Override
public int getCount() {
return _total_number_of_weeks;
}
};
Then WeekDaysFragment
can easily display the week starting at the date passed in its arguments.
Alternatively, it seems that some version of the Calendar app on Android uses a ViewSwitcher
(which means there's only 2 pages, the one you see and the hidden page). It then changes the transition animation based on which way the user swiped and renders the next/previous page accordingly. In this way you get infinite pagination because it just switching between two pages infinitely. This requires using a View
for the page though, which is way I went with the first approach.
In general, if you want "infinite pagination", it's probably because your pages are based off of dates or times somehow. If this is the case consider using a finite subset of time that is relatively infinite instead. This is how CalendarView
is implemented for example. Or you can use the ViewSwitcher
approach. The advantage of these two approaches is that neither does anything particularly unusual with the ViewSwitcher
or ViewPager
, and doesn't require any tricks or reimplementation to coerce them to behave infinitely (ViewSwitcher
is already designed to switch between views infinitely, but ViewPager
is designed to work on a finite, but not necessarily constant, set of pages).
![](../../users/profiles/910718.webp)
- 5,795
- 5
- 39
- 55
All you need to do is look at the example here
You will find that in line 295 the page is always set to 1 so that it is scrollable
and that the count of pages is 3 in getCount()
method.
Those are the 2 main things you need to change, the rest is your logic and you can handle them differently.
Just make a personal counter that counts the real page you are on because position will no longer be usable after always setting current page to 1 on line 295.
p.s. this code is not mine it was referenced in the question you linked in your question
![](../../users/profiles/1116580.webp)
- 51
- 6
![](../../users/profiles/435706.webp)
- 3,303
- 7
- 37
- 58
infinite slider adapter skeleton based on previous samples
some critical issues:
- remember original (relative) position in page view (tag used in sample), so we will look this position to define relative position of view. otherwise child order in pager is mixed
- have to fill first time absolute view inside adapter. (the rest of times this fill will be invalid) found no way to force it fill from pager handler. the rest times absolute view will be overriden from pager handler with correct values.
- when pages are slided quickly, side page (actually left) is not filled from pager handler. no workaround for the moment, just use empty view, it will be filled with actual values when drag is stopped. upd: quick workaround: disable adapter's destroyItem.
you may look at the logcat to understand whats happening in this sample
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/calendar_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:padding="5dp"
android:layout_gravity="center_horizontal"
android:text="Text Text Text"
/>
</RelativeLayout>
And then:
public class ActivityCalendar extends Activity
{
public class CalendarAdapter extends PagerAdapter
{
@Override
public int getCount()
{
return 3;
}
@Override
public boolean isViewFromObject(View view, Object object)
{
return view == ((RelativeLayout) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position)
{
LayoutInflater inflater = (LayoutInflater)ActivityCalendar.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View viewLayout = inflater.inflate(R.layout.layout_calendar, container, false);
viewLayout.setTag(new Integer(position));
//TextView tv = (TextView) viewLayout.findViewById(R.id.calendar_text);
//tv.setText(String.format("Text Text Text relative: %d", position));
if (!ActivityCalendar.this.scrolledOnce)
{
// fill here only first time, the rest will be overriden in pager scroll handler
switch (position)
{
case 0:
ActivityCalendar.this.setPageContent(viewLayout, globalPosition - 1);
break;
case 1:
ActivityCalendar.this.setPageContent(viewLayout, globalPosition);
break;
case 2:
ActivityCalendar.this.setPageContent(viewLayout, globalPosition + 1);
break;
}
}
((ViewPager) container).addView(viewLayout);
//Log.i("instantiateItem", String.format("position = %d", position));
return viewLayout;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object)
{
((ViewPager) container).removeView((RelativeLayout) object);
//Log.i("destroyItem", String.format("position = %d", position));
}
}
public void setPageContent(View viewLayout, int globalPosition)
{
if (viewLayout == null)
return;
TextView tv = (TextView) viewLayout.findViewById(R.id.calendar_text);
tv.setText(String.format("Text Text Text global %d", globalPosition));
}
private boolean scrolledOnce = false;
private int focusedPage = 0;
private int globalPosition = 0;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calendar);
final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setOnPageChangeListener(new OnPageChangeListener()
{
@Override
public void onPageSelected(int position)
{
focusedPage = position;
// actual page change only when position == 1
if (position == 1)
setTitle(String.format("relative: %d, global: %d", position, globalPosition));
Log.i("onPageSelected", String.format("focusedPage/position = %d, globalPosition = %d", position, globalPosition));
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
//Log.i("onPageScrolled", String.format("position = %d, positionOffset = %f", position, positionOffset));
}
@Override
public void onPageScrollStateChanged(int state)
{
Log.i("onPageScrollStateChanged", String.format("state = %d, focusedPage = %d", state, focusedPage));
if (state == ViewPager.SCROLL_STATE_IDLE)
{
if (focusedPage == 0)
globalPosition--;
else if (focusedPage == 2)
globalPosition++;
scrolledOnce = true;
for (int i = 0; i < viewPager.getChildCount(); i++)
{
final View v = viewPager.getChildAt(i);
if (v == null)
continue;
// reveal correct child position
Integer tag = (Integer)v.getTag();
if (tag == null)
continue;
switch (tag.intValue())
{
case 0:
setPageContent(v, globalPosition - 1);
break;
case 1:
setPageContent(v, globalPosition);
break;
case 2:
setPageContent(v, globalPosition + 1);
break;
}
}
Log.i("onPageScrollStateChanged", String.format("globalPosition = %d", globalPosition));
viewPager.setCurrentItem(1, false);
}
}
});
CalendarAdapter calendarAdapter = this.new CalendarAdapter();
viewPager.setAdapter(calendarAdapter);
// center item
viewPager.setCurrentItem(1, false);
}
}
![](../../users/profiles/717214.webp)
- 52,841
- 25
- 149
- 233
![](../../users/profiles/3285559.webp)
- 86
- 3
Its hacked by CustomPagerAdapter:
MainActivity.java:
import android.content.Context;
import android.os.Handler;
import android.os.Parcelable;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private List<String> numberList = new ArrayList<String>();
private CustomPagerAdapter mCustomPagerAdapter;
private ViewPager mViewPager;
private Handler handler;
private Runnable runnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
numberList.clear();
for (int i = 0; i < 10; i++) {
numberList.add(""+i);
}
mViewPager = (ViewPager)findViewById(R.id.pager);
mCustomPagerAdapter = new CustomPagerAdapter(MainActivity.this);
EndlessPagerAdapter mAdapater = new EndlessPagerAdapter(mCustomPagerAdapter);
mViewPager.setAdapter(mAdapater);
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
int modulo = position%numberList.size();
Log.i("Current ViewPager View's Position", ""+modulo);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
handler = new Handler();
runnable = new Runnable() {
@Override
public void run() {
mViewPager.setCurrentItem(mViewPager.getCurrentItem()+1);
handler.postDelayed(runnable, 1000);
}
};
handler.post(runnable);
}
@Override
protected void onDestroy() {
if(handler!=null){
handler.removeCallbacks(runnable);
}
super.onDestroy();
}
private class CustomPagerAdapter extends PagerAdapter {
Context mContext;
LayoutInflater mLayoutInflater;
public CustomPagerAdapter(Context context) {
mContext = context;
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return numberList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == ((LinearLayout) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View itemView = mLayoutInflater.inflate(R.layout.row_item_viewpager, container, false);
TextView textView = (TextView) itemView.findViewById(R.id.txtItem);
textView.setText(numberList.get(position));
container.addView(itemView);
return itemView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
private class EndlessPagerAdapter extends PagerAdapter {
private static final String TAG = "EndlessPagerAdapter";
private static final boolean DEBUG = false;
private final PagerAdapter mPagerAdapter;
EndlessPagerAdapter(PagerAdapter pagerAdapter) {
if (pagerAdapter == null) {
throw new IllegalArgumentException("Did you forget initialize PagerAdapter?");
}
if ((pagerAdapter instanceof FragmentPagerAdapter || pagerAdapter instanceof FragmentStatePagerAdapter) && pagerAdapter.getCount() < 3) {
throw new IllegalArgumentException("When you use FragmentPagerAdapter or FragmentStatePagerAdapter, it only supports >= 3 pages.");
}
mPagerAdapter = pagerAdapter;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (DEBUG) Log.d(TAG, "Destroy: " + getVirtualPosition(position));
mPagerAdapter.destroyItem(container, getVirtualPosition(position), object);
if (mPagerAdapter.getCount() < 4) {
mPagerAdapter.instantiateItem(container, getVirtualPosition(position));
}
}
@Override
public void finishUpdate(ViewGroup container) {
mPagerAdapter.finishUpdate(container);
}
@Override
public int getCount() {
return Integer.MAX_VALUE; // this is the magic that we can scroll infinitely.
}
@Override
public CharSequence getPageTitle(int position) {
return mPagerAdapter.getPageTitle(getVirtualPosition(position));
}
@Override
public float getPageWidth(int position) {
return mPagerAdapter.getPageWidth(getVirtualPosition(position));
}
@Override
public boolean isViewFromObject(View view, Object o) {
return mPagerAdapter.isViewFromObject(view, o);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (DEBUG) Log.d(TAG, "Instantiate: " + getVirtualPosition(position));
return mPagerAdapter.instantiateItem(container, getVirtualPosition(position));
}
@Override
public Parcelable saveState() {
return mPagerAdapter.saveState();
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
mPagerAdapter.restoreState(state, loader);
}
@Override
public void startUpdate(ViewGroup container) {
mPagerAdapter.startUpdate(container);
}
int getVirtualPosition(int realPosition) {
return realPosition % mPagerAdapter.getCount();
}
PagerAdapter getPagerAdapter() {
return mPagerAdapter;
}
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="180dp">
</android.support.v4.view.ViewPager>
</RelativeLayout>
row_item_viewpager.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/txtItem"
android:textAppearance="@android:style/TextAppearance.Large"/>
</LinearLayout>
Done
![](../../users/profiles/4233197.webp)
- 48,538
- 20
- 161
- 144
-
`MAX_INTEGER` will make `ArrayList
mItems` inside `ViewPager` to grow. Also further you scroll - time for call to `ViewPager.populate` will be increased as it loops over `mItems` to find cached `ItemInfo` for current possition. – j2ko Sep 08 '16 at 11:15 -
try to use FragmentStatePagerAdapter, it only keeps three fragments previous, current and next, each time you scroll back and forth the adapter will recycle his fragments/layouts. – Anton Makov Mar 23 '17 at 02:25
For infinite scrolling with days it's important you have the good fragment in the pager therefore I wrote my answer on this on page (Viewpager in Android to switch between days endlessly)
It's working very well! Above answers did not work for me as I wanted it to work.
![](../../users/profiles/-1.webp)
- 1
- 1
![](../../users/profiles/2508481.webp)
- 1,741
- 16
- 32
I built a library that can make any ViewPager, pagerAdapter (or FragmentStatePagerAdapter), and optional TabLayout infinitely Scrolling.
https://github.com/memorex386/infinite-scroll-viewpager-w-tabs
![](../../users/profiles/4722059.webp)
- 210
- 4
- 6
Based on https://github.com/antonyt/InfiniteViewPager I wrote up this which works nicely:
class InfiniteViewPager @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : ViewPager(context, attrs) {
// Allow for 100 back cycles from the beginning.
// This should be enough to create an illusion of infinity.
// Warning: scrolling to very high values (1,000,000+) results in strange drawing behaviour.
private val offsetAmount get() = if (adapter?.count == 0) 0 else (adapter as InfinitePagerAdapter).realCount * 100
override fun setAdapter(adapter: PagerAdapter?) {
super.setAdapter(if (adapter == null) null else InfinitePagerAdapter(adapter))
currentItem = 0
}
override fun setCurrentItem(item: Int) = setCurrentItem(item, false)
override fun setCurrentItem(item: Int, smoothScroll: Boolean) {
val adapterCount = adapter?.count
if (adapterCount == null || adapterCount == 0) {
super.setCurrentItem(item, smoothScroll)
} else {
super.setCurrentItem(offsetAmount + item % adapterCount, smoothScroll)
}
}
override fun getCurrentItem(): Int {
val adapterCount = adapter?.count
return if (adapterCount == null || adapterCount == 0) {
super.getCurrentItem()
} else {
val position = super.getCurrentItem()
position % (adapter as InfinitePagerAdapter).realCount
}
}
fun animateForward() {
super.setCurrentItem(super.getCurrentItem() + 1, true)
}
fun animateBackwards() {
super.setCurrentItem(super.getCurrentItem() - 1, true)
}
internal class InfinitePagerAdapter(private val adapter: PagerAdapter) : PagerAdapter() {
internal val realCount: Int get() = adapter.count
override fun getCount() = if (realCount == 0) 0 else Integer.MAX_VALUE
override fun instantiateItem(container: ViewGroup, position: Int) = adapter.instantiateItem(container, position % realCount)
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) = adapter.destroyItem(container, position % realCount, `object`)
override fun finishUpdate(container: ViewGroup) = adapter.finishUpdate(container)
override fun isViewFromObject(view: View, `object`: Any) = adapter.isViewFromObject(view, `object`)
override fun restoreState(bundle: Parcelable?, classLoader: ClassLoader?) = adapter.restoreState(bundle, classLoader)
override fun saveState(): Parcelable? = adapter.saveState()
override fun startUpdate(container: ViewGroup) = adapter.startUpdate(container)
override fun getPageTitle(position: Int) = adapter.getPageTitle(position % realCount)
override fun getPageWidth(position: Int) = adapter.getPageWidth(position)
override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) = adapter.setPrimaryItem(container, position, `object`)
override fun unregisterDataSetObserver(observer: DataSetObserver) = adapter.unregisterDataSetObserver(observer)
override fun registerDataSetObserver(observer: DataSetObserver) = adapter.registerDataSetObserver(observer)
override fun notifyDataSetChanged() = adapter.notifyDataSetChanged()
override fun getItemPosition(`object`: Any) = adapter.getItemPosition(`object`)
}
}
For consuming it simply change your ViewPager to InfiniteViewPager and that's all you need to change.
![](../../users/profiles/1979703.webp)
- 18,855
- 28
- 114
- 153