I am using a ViewPager
with the FragmentStatePagerAdapter
to allow navigation between some fragments.
Let's say I have three fragments: A
, B
and C
. The ViewPager shows Fragment A initially, and allows you to navigate to Fragment B by swiping from right-to-left, and then to Fragment C by swiping again. This allows the following navigation paths: A <--> B <--> C
.
What I would like is to be able to swipe from left-to-right on Fragment A and have the ViewPager show Fragment C, i.e. for it to behave as a circular queue and allow ... --> C <--> A <--> B <--> C <--> A <-- ...
I do not want the Fragments duplicated in other positions (i.e. ending up with more than three instances).
Is this wrapping functionality possible with a ViewPager?
I've implemented a ViewPager/PagerAdapter that can allow for pseudo-infinite paging behaviour. It works by specifying a very large number as the actual count, but maps them to the actual range of the dataset/pageset. It offsets the beginning by a large number also so that you can immediately scroll to the left from the 'first' page.
It doesn't work so well once you get to 1,000,000th page (you will see graphical glitches when scrolling), but this is typically not a real-world use-case. I could fix this by resetting the count to a lower number once in a while, but I will leave it how it is for now.
The InfinitePagerAdapter
wraps an existing ViewPager, and so the usage is quite transparent. The InfiniteViewPager
does does a bit of work to make sure you can potentially scroll to the left and right many times.
https://github.com/antonyt/InfiniteViewPager
This is a solution without fake pages and works like a charm:
public class CircularViewPagerHandler implements ViewPager.OnPageChangeListener {
private ViewPager mViewPager;
private int mCurrentPosition;
private int mScrollState;
public CircularViewPagerHandler(final ViewPager viewPager) {
mViewPager = viewPager;
}
@Override
public void onPageSelected(final int position) {
mCurrentPosition = position;
}
@Override
public void onPageScrollStateChanged(final int state) {
handleScrollState(state);
mScrollState = state;
}
private void handleScrollState(final int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
setNextItemIfNeeded();
}
}
private void setNextItemIfNeeded() {
if (!isScrollStateSettling()) {
handleSetNextItem();
}
}
private boolean isScrollStateSettling() {
return mScrollState == ViewPager.SCROLL_STATE_SETTLING;
}
private void handleSetNextItem() {
final int lastPosition = mViewPager.getAdapter().getCount() - 1;
if(mCurrentPosition == 0) {
mViewPager.setCurrentItem(lastPosition, false);
} else if(mCurrentPosition == lastPosition) {
mViewPager.setCurrentItem(0, false);
}
}
@Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
}
}
You just have to set it to your ViewPager as onPageChangeListener and that's it: ( ** deprecated now ** check the edit notes)
viewPager.setOnPageChangeListener(new CircularViewPagerHandler(viewPager));
To avoid having this blue shine at the "end" of your ViewPager you should apply this line to your xml where the ViewPager is placed:
android:overScrollMode="never"
We have improved the solution above and created a little library on github. Feel free to check it out :)
Edit:: As per the @Bhavana comment , Just use addOnPageChangeListener instead of setOnPageChangeListener as later is deprecated.
viewPager.addOnPageChangeListener(new CircularViewPagerHandler(viewPager));
I've been trying all the suggestions, solutions, libraries, etc but they're not pure circular and most of the time don't have support for only 3 pages.
So I implemented a circular ViewPager
example using the new ViewPager2
, the new ViewPager
uses a RecyclerView
and ViewHolder
s to handle the views recycling and works as expected!
TLDR: GITHUB
In this example, will be building a single activity app with ViewPager2
and a FragmentPagerAdapter
supporting circular navigation between 3 pages or more.
I'm using an alpha version of the library androidx.viewpager2:viewpager2
, but the version 1.0.0-alpha06
is the last one planned before google freezing the API and moving to beta.
1. Add the ViewPager2
library to the dependencies in your build.gradle
dependencies {
implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha06'
}
2. Add the ViewPager2
view to your project:
<androidx.viewpager2.widget.ViewPager2 xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/vwpHome"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3. Create the FragmentStateAdapter
adapter:
getItemCount()
needs to returns a huuuuge number. (2147483647)
getCenterPage()
returns the central page based on the huuuuge number.
This method is used to get the position of the initial page to set in the ViewPager2
, in this case the user needs to swipe ˜1073741823 time to reach the end of the ViewPager2
.
class CircularPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount() = Integer.MAX_VALUE
/**
* Create the fragment based on the position
*/
override fun createFragment(position: Int) = HomePagerScreens.values()[position % HomePagerScreens.values().size].fragment.java.newInstance()
/**
* Returns the same id for the same Fragment.
*/
override fun getItemId(position: Int): Long = (position % HomePagerScreens.values().size).toLong()
fun getCenterPage(position: Int = 0) = Integer.MAX_VALUE / 2 + position
}
HomeScreens is a ENUM with the page info.
enum class HomePagerScreens(@StringRes val title: Int,
val fragment: KClass<out Fragment>) {
HOME_1(R.string.home_1, FragmentHome::class),
HOME_2(R.string.home_2, FragmentHome::class),
HOME_3(R.string.home_3, FragmentHome::class)
}
4. Set the adapter to the ViewPager
val circularAdapter = CircularPagerAdapter(supportFragmentManager, lifecycle)
vwpHome.apply {
adapter = circularAdapter
setCurrentItem(circularAdapter.getCenterPage(), false)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With