ViewPager as a circular queue / wrapping
Asked Answered
H

14

64

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?

Hensel answered 25/9, 2011 at 14:42 Comment(7)
It's not built-in to ViewPager, but you might try modifying FragmentStatePagerAdapter.instantiateItem to use some modulus math to load the proper Fragment. You'll need to override getCount to return a sufficiently large number.Discard
@Hensel Were you able to find a solution to this?Scoundrelly
@BryanDenny not yet, I've actually transitioned my app to a different design for now. I will post back if I get a working solution to the original problem.Hensel
@Enigma please see my answer below.Hensel
@BryanDenny you too! [can only @-notify one person per post]Hensel
You might also want to take a look at #8239556 I have posted an answer that requires only 2 dummy pages.Diaconate
Too bad ViewPager.mCurPos is private...Punjabi
H
49

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

Hensel answered 11/3, 2012 at 16:51 Comment(7)
Wow this is great. I made it into a pseudo-infinite (365 actually) pager. Testing now with TitlePageIndicator. Thanks a lotUnofficial
Awesome..Lol! Why did I not think of it...Definitely +1Nagle
It has problem when the number of pages are less than 4.Bolger
Hello, antonyt. How to fix less then 4 fragments? I saw your answer here github.com/antonyt/InfiniteViewPager/issues/2 could you help me? I need to swap 3 fragments, but I couldn't fix this bug. How to block destroyItem() / calling instantiateItem() ?Restive
java.lang.IllegalStateException: Fragment already added: I got that exception. I am using less then 3 pages.Wolfson
Hi antonyt, How implement Title page indicator with infinatePageadapter, please help me?Vaishnava
@Bandreid, did you implement the Title page indicator with it... can you please help me... by sharing your experience?Vaishnava
B
19

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));
Beech answered 8/6, 2015 at 9:26 Comment(7)
great and simple, but it is not slidingLopez
Worked out well for me, I followed the project on Github. Perfect thanks.Glisson
happy to help :) enjoy codingBeech
really a geat help... tysm :)Shaia
Worked for me too, Thanks!!Busboy
this doesnt work in tabs any workaround ? @KingofMassesBroccoli
Works perfectly..!! Just use addOnPageChangeListener instead of setOnPageChangeListener as later is deprecated.. Thanks ..Promptitude
J
10

Another way to do this is to add a fake copy of the first element in the view pager to the end of your adapter, and a fake copy of the last element to the beginning. Then when you are viewing the first/last element in the view pager, change pages without animating.

So basically, the first/last elements in your view pager are fakes, just there to produce the correct animation, and then switch over to the end/beginning. Example:

list.add(list.get(0)); //add the first item again as the last, to create the wrap effect
list.add(0, list.get(list.size()-2)); //insert a copy of the last item as the new first item, so we can scroll backwards too

viewPager.setAdapter(list);
viewPager.setCurrentItem(1, false); //default to the second element (because the first is now a fake)

viewPager.setOnPageChangeListener(new OnPageChangeListener() {
private void handleScrollState(final int state) {
        if (state == ViewPager.SCROLL_STATE_IDLE) { //this is triggered when the switch to a new page is complete
            final int lastPosition = viewPager.getAdapter().getCount() - 1;
            if (currentPosition == lastPosition) {
                viewPager.setCurrentItem(1, false); //false so we don't animate
            } else if (currentPosition == 0) {
                viewPager.setCurrentItem(lastPosition -1, false);
            }
        }

}
Jessalin answered 29/8, 2014 at 19:9 Comment(2)
Works nicely. Simple and clean solution.Knowle
Yet you might struggle with some bug, if you have infinite scrolling and custom PageTransformer transitions. Some transitions break a specific view after making a transition without animation( like viewPager.setCurrentItem(1, false); ). Nonetheless, if you have a simple pager, this works.Knowle
M
10

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 ViewHolders 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.

enter image description here

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)
}
Massage answered 12/7, 2019 at 9:58 Comment(2)
This should be the only accepted answer, in my opinion. Thanks a lot, @extmkv! :)Tolman
hi. when I tried to set getItemCount() = Integer.MAX_VALUE , I can not go to the screen which contains viewpager. There is no logcat so I dont know what happeningBartizan
S
9

Just now saw this question and found a solution. Solution Link

Make the following changes to your onPageScrollStateChanged function. Consider you have 4 tabs.

private int previousState, currentState;

public void onPageScrollStateChanged(int state) {              

            int currentPage = viewPager.getCurrentItem();       //ViewPager Type
            if (currentPage == 3 || currentPage == 0){
                previousState = currentState;
                currentState = state;
                if (previousState == 1 && currentState == 0){

                    viewPager.setCurrentItem(currentPage == 0 ? 3 : 0);

                }

            }

        }

If the state variable is in the existing tabs, its value is 1.

It becomes 0 once you try to navigate before your first tab or after your last tab. So, compare the previous and current states, as shown in the code and you are done.

See onPageScrollStateChanged function here.

Hope it helps.

Sims answered 27/1, 2014 at 10:14 Comment(0)
T
6

This almost accomplishes what antonyt wants, it wont let you go from A -> C though. (Not tested vigorously, but seems to work well enough)

public class MyAdapter extends PagerAdapter {
    private List<Object> mItems;
    private int mFakeCount = 0;

    public MyAdapter(Context context, List<Object> items) {
        mItems = items;
        mFakeCount = mItems.size()+1;
    }

    @Override
    public int getCount() {
        return mFakeCount;
    }

    @Override
    public Object instantiateItem(View collection, int position) {
        // make the size larger, and change the position
        // to trick viewpager into paging forever
        if (position >= mItems.size()-1) {
            int newPosition = position%mItems.size();

            position = newPosition;
            mFakeCount++;
        }

        // do your layout inflating and what not here.
        // position will now refer to a correct index into your mItems list
        // even if the user has paged so many times that the view has wrapped
    }
}
Thinker answered 29/11, 2011 at 0:55 Comment(2)
It WILL let you go from A -> C if you call viewPager.setCurrentItem((int) (adapter.getCount()/2), false); after setting the adapter. This way, your ViewPager will initialize to middle position, so you can scroll in both directions.Miguelinamiguelita
I'm confused how this worked for you guys. It didn't work for me... [AndroidRuntime] FATAL EXCEPTION: main [AndroidRuntime] java.lang.IllegalStateException: The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: 4, found: 5 Pager id: com.rpr.mobile:id/ambient_pager Pager class: class android.support.v4.view.ViewPager Problematic adapter: class rpr.mobile.droid.adapters.ambient.AmbientPagerAdapter [AndroidRuntime] at android.support.v4.view.ViewPager.populate(ViewPager.java:962)Goddamn
A
2

Simple idea how to achieve this. When you have your Array with pages simply add this array to another array exactly in the middle.

For example you have array with 15 elements. So place it into 400 element array in the middle (200 position)

Next simply fill array to the left and right so you can move around.

Sample image:

enter image description here

How it looks?

https://youtu.be/7up36ylTzXk

Let the code talk :)

https://gist.github.com/gelldur/051394d10f6f98e2c13d

public class CyclicPagesAdapter extends FragmentStatePagerAdapter {

    public CyclicPagesAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        return _fragments.get(position).createInstance();
    }

    @Override
    public int getCount() {
        return _fragments.size();
    }

    public void addFragment(FragmentCreator creator) {
        _fragments.add(creator);
    }

    public interface FragmentCreator {
        Fragment createInstance();
    }

    public void clear() {
        _fragments.clear();
    }

    public void add(FragmentCreator fragmentCreator) {
        _fragments.add(fragmentCreator);
    }

    public void finishAdding() {
        ArrayList<FragmentCreator> arrayList = new ArrayList<>(Arrays.asList(new FragmentCreator[MAX_PAGES]));
        arrayList.addAll(MAX_PAGES / 2, _fragments);

        int mrPointer = _fragments.size() - 1;
        for (int i = MAX_PAGES / 2 - 1; i > -1; --i) {
            arrayList.set(i, _fragments.get(mrPointer));
            --mrPointer;
            if (mrPointer < 0) {
                mrPointer = _fragments.size() - 1;
            }
        }

        mrPointer = 0;
        for (int i = MAX_PAGES / 2 + _fragments.size(); i < arrayList.size(); ++i) {
            arrayList.set(i, _fragments.get(mrPointer));
            ++mrPointer;
            if (mrPointer >= _fragments.size()) {
                mrPointer = 0;
            }
        }
        _fragmentsRaw = _fragments;
        _fragments = arrayList;
    }

    public int getStartIndex() {
        return MAX_PAGES / 2;
    }

    public int getRealPagesCount() {
        return _fragmentsRaw.size();
    }

    public int getRealPagePosition(int index) {
        final FragmentCreator fragmentCreator = _fragments.get(index);
        for (int i = 0; i < _fragmentsRaw.size(); ++i) {
            if (fragmentCreator == _fragmentsRaw.get(i)) {
                return i;
            }
        }
        //Fail...
        return 0;
    }

    private static final int MAX_PAGES = 500;
    public ArrayList<FragmentCreator> _fragments = new ArrayList<>();
    public ArrayList<FragmentCreator> _fragmentsRaw;
}
Alexandra answered 6/10, 2015 at 22:11 Comment(8)
Very nice and clean solution. Thank you. I have just one question. Why you decided to use an interface to add fragments? Isn't it easier to add them via the fanction for example addFragment(Fragment fragment)? And then holding a List of Fragments?Weisman
@Weisman i don't wan't store heavy fragments in memory so i do lazy initialization.Alexandra
If it's not a problem I would like to ask you one more question. What about removing an item? Is this approach: pastebin.com/VKkY4j6Z ok? Now I'm getting java.util.ConcurrentModificationException error, so maybe it's not a best solution.Weisman
@Weisman not a problem but you must remove it other way. During iteration your iterators will be malformed when you delete. Simply https://mcmap.net/q/36388/-iterating-through-a-collection-avoiding-concurrentmodificationexception-when-removing-objects-in-a-loop/1052261Alexandra
I've got one last question. Do you have an idea how to correctly remove item form Adapter? I would like to remove an item and after that jump to next item (for example, I'm removing item number 6/30 and then next displayed item should be 6/29). I know I should remove all items form fragments list. After notifyDataSetChanged the wrong item will be selected because whole indexing is changed. My idea was to count how many items was removed on the "left" of removed item and then scroll to position - numberOfRemovedItemsOnLeft but it works in some cases and in some cases it doesn't.Weisman
@Weisman why simply you don't remember ID of your current fragment? It is cyclic adapter so no matter is it index 100 or index 10 if it is the same fragment :) Simply after notifyData set changed jump to first ID you wantAlexandra
I don't know if I get your idea :) I'm removing current fragment, then I'm removing all framgnets from fragments list (for be sure that they are removed in all cycles). Then I'm doing notifyDataSetChanged and the whole indexing is changed, so I can't just jump to position + 1 item because it's probably different fragment than was previously. I've got sorted data in fragments and these fragments has their indicating number, so I can't jump to random fragment :)Weisman
You can use position in _fragmentsRaw Or simply add HashMap for index - fragment name what do you wantAlexandra
T
0

I have a solution I believe will work. It isn't tested, but here it is:

A wrapper around FragmentPagerAdapter as the super class:

public abstract class AFlexibleFragmentPagerAdapter extends
        FragmentPagerAdapter
{
    public AFlexibleFragmentPagerAdapter(FragmentManager fm)
    {
        super(fm);
        // TODO Auto-generated constructor stub
    }

    public abstract int getRealCount();

    public abstract int getStartPosition();

    public abstract int transformPosition(int position);
};

Then the standard FragmentPagerAdapter implementation subclass this new abstract class:

public class SomeFragmentPagerAdapter extends
        AFlexibleFragmentPagerAdapter
{

    private Collection<Fragment> someContainer;

    public SomeFragmentPagerAdapter(FragmentManager fm, Container<Fragment> fs)
    {
        super(fm);
        someContainer = fs;
    }

    @Override
    public int getRealCount() { return someContainer.size(); }

    @Override
    public int getStartPosition() { return 0; }

    @Override
    public int transformPosition(int position) { return position; }
};

And the Cyclic Pager Adapter implementation.

public class SomeCyclicFragmentPagerAdapter extends
        SomeFragmentPagerAdapter 
{
    private int realCount = 0;
    private int dummyCount = 0;
    private static final int MULTI = 3;

    public SomeFragmentPagerAdapter(FragmentManager fm, ...)
    {
        super(fm);
        realCount = super.getCount();
        dummyCount *= MULTI;
    }

    @Override
    public int getCount() { return dummyCount; }

    @Override
    public int getRealCount() { return realCount; }

    @Override
    public int getStartPosition() { return realCount; }

    @Override
    public int transformPosition(int position)
    {
        if (position < realCount) position += realCount;
        else if (position >= (2 * realCount)) position -= realCount;

        return position;
    }
};

ViewPager initialization

    this.mViewPager.setCurrentItem(this.mPagerAdapter.getStartPosition());

And finally, the callback implementation:

    @Override
    public void onPageSelected(int position)
    {
        this.mTabHost.setCurrentTab(position % mPagerAdapter.getRealCount());
        this.mViewPager.setCurrentItem(this.mPagerAdapter
                .transformPosition(position));
    }

    @Override
    public void onTabChanged(String tabId)
    {
        int pos = this.mTabHost.getCurrentTab();

        this.mViewPager.setCurrentItem(this.mPagerAdapter
                .transformPosition(pos));
    }
Therapist answered 3/8, 2013 at 18:58 Comment(0)
G
0
  1. Set for the items count some large value, say 500000.
  2. Return from adapter this value * number of actual items.
  3. On getItem(int i) adjust the i to -> i = i % number of actual items.
  4. On onCreate(Bundle savedInstanceState) set current item for page 500000/2

    public class MainActivity extends FragmentActivity {
    
        private final static int ITEMS_MAX_VALUE = 500000;
        private int actualNumCount = 10;
        private ViewPager mPager = null;
    
        private class OptionPageAdapter extends FragmentStatePagerAdapter {
            public OptionPageAdapter(FragmentManager fm) {
                super(fm);
            }
    
            @Override
            public Fragment getItem(int i) {
                try {
                    i = i % OptionType.values().length;
                    OptionPage optionPage = new OptionPage(i);
                    optionPage.id = i;
                    return optionPage;
                } catch (Exception e) {
                    VayoLog.log(Level.WARNING, "Unable to create OptionFragment for id: " + i, e);
                }
                return null;
            }
    
            @Override
            public int getCount() {
                return ITEMS_MAX_VALUE * actualNumCount;
            }
        }
    
        public static class OptionPage extends Fragment {
            private int id = 0
            @Nullable
            @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                View mainView = inflater.inflate(R.layout.main_page, container, false);
                return mainView;
            }
    
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            mPager = (ViewPager) findViewById(R.id.main_pager);
            mPager.setOffscreenPageLimit(3);
            mPager.setAdapter(new OptionPageAdapter(this.getSupportFragmentManager()));
            mPager.setCurrentItem(ITEMS_MAX_VALUE/2,false);
    
        }
    
    }
    
Gondolier answered 15/12, 2015 at 15:15 Comment(0)
N
0
  1. add fake copy of the Last Element at the begining of your list.
  2. add fake copy of the First Element at the end.
  3. select real First Element somewhere in init code:

        mViewPager.setCurrentItem(1);
    
  4. modify setOnPageChangeListener:

        @Override
        public void onPageSelected(int state) {
            if (state == ITEMS_NUMBER + 1) {
                mViewPager.setCurrentItem(1, false);
            } else if (state == 0) {
                mViewPager.setCurrentItem(ITEMS_NUMBER, false);
            }
        }
    
Nolannolana answered 13/5, 2016 at 14:58 Comment(0)
N
0

based on this approach

true loop, pager title strip, true no refresh scrolling

private static final int ITEM_NUM = 5 ; // 5 for smooth pager title strip
private void addFragment(String title, String className) {
    if (fragments.size() < ITEM_NUM) {
        Bundle b = new Bundle();
        b.putInt("pos", fragments.size());
        fragments.add(Fragment.instantiate(this, className, b));

    }
    titles.add(title);
    itemList.add(className);
}

view page adapter:

public static class MyFragmentPageAdapter extends FragmentPagerAdapter {

    private HashMap<Long, Fragment> mItems = new HashMap<Long, Fragment>();

    public MyFragmentPageAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    @Override
    public long getItemId(int position) {
        int id = java.lang.System.identityHashCode(fragments.get(position));
        return id;
    }

    @Override
    public Fragment getItem(int position) {
        long id = getItemId(position);
        if (mItems.get(id) != null) {
            return mItems.get(id);
        }
        Fragment f = fragments.get(position);
        return f;
    }

    @Override
    public int getCount() {
        return ITEM_NUM;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        String t = titles.get(getItem(position).getArguments()
                .getInt("pos"));
        return t;
    }

    @Override
    public int getItemPosition(Object object) {
        for (int i = 0; i < ITEM_NUM; i++) {
            Fragment item = (Fragment) fragments.get(i);
            if (item.equals((Fragment) object)) {
                return i;
            }
        }
        return POSITION_NONE;
    }
}

using:

    itemList = new ArrayList<String>();
    fragments = new ArrayList<Fragment>();
    titles = new ArrayList<String>();

    addFragment("Title1", Fragment1.class.getName());
    ...
    addFragment("TitleN", FragmentN.class.getName());

    mAdapter = new MyFragmentPageAdapter(getSupportFragmentManager());
    mViewPager = (ViewPager) findViewById(R.id...);

    mViewPager.setAdapter(mAdapter);
    mViewPager.setCurrentItem(2);
    currPage = 2;
    visibleFragmentPos = 2;

    mViewPager.setOnPageChangeListener(new OnPageChangeListener() {

        @Override
        public void onPageSelected(int curr) {
            dir = curr - currPage;
            currPage = curr;
        }

        private void shiftRight() {
            fragments.remove(0);
            int pos = visibleFragmentPos + 3;
            if (pos > itemList.size() - 1)
                pos -= itemList.size();
            if (++visibleFragmentPos > itemList.size() - 1)
                visibleFragmentPos = 0;
            insertItem(4, pos);

        }

        private void shiftLeft() {
            fragments.remove(4);
            int pos = visibleFragmentPos - 3;
            if (pos < 0)
                pos += itemList.size();
            if (--visibleFragmentPos < 0)
                visibleFragmentPos = itemList.size() - 1;
            insertItem(0, pos);
        }

        private void insertItem(int datasetPos, int listPos) {
            Bundle b = new Bundle();
            b.putInt("pos", listPos);
            fragments.add(datasetPos,
                    Fragment.instantiate(ctx, itemList.get(listPos), b));
        }

        @Override
        public void onPageScrollStateChanged(int state) {

            if (state == ViewPager.SCROLL_STATE_IDLE) {
                if (dir == 1) {
                    shiftRight();
                } else if (dir == -1) {
                    shiftLeft();

                }
                mAdapter.notifyDataSetChanged();
                currPage = 2;
            }
        }
    });
Nolannolana answered 4/7, 2016 at 8:54 Comment(0)
A
0

You may use simple view from https://github.com/pozitiffcat/cyclicview
One of features is:

Do not duplicate first and last views, used bitmap variants instead

Example:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CyclicView cyclicView = (CyclicView) findViewById(R.id.cyclic_view);
        cyclicView.setAdapter(new CyclicAdapter() {
            @Override
            public int getItemsCount() {
                return 10;
            }

            @Override
            public View createView(int position) {
                TextView textView = new TextView(MainActivity.this);
                textView.setText(String.format("TextView #%d", position + 1));
                return textView;
            }

            @Override
            public void removeView(int position, View view) {
                // Do nothing
            }
        });
    }
}
Ascension answered 13/10, 2016 at 14:18 Comment(0)
B
0

My answer is probably similar to others out there. However I couldn't find a solution in ViewPager2 with FragmentStateAdapter.

public class ScreenSlidePagerAdapter extends FragmentStateAdapter {
    private final Fragment[] fragments = {new Fragment4(), new Fragment1(),
            new Fragment2(), new Fragment3(),
            new Fragment4(), new Fragment1()};

    public ScreenSlidePagerAdapter(FragmentActivity fa) { super(fa); }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return fragments[position];
    }

    @Override
    public int getItemCount() { return fragments.length; }
}

Where Fragment1 is the first to be displayed. The in your MainActivity (or whichever activity has the ViewPager):

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        ViewPager2 viewPager = findViewById(R.id.viewpager);
        ScreenSlidePagerAdapter adapter = new ScreenSlidePagerAdapter(this);
        viewPager.setAdapter(adapter);
        viewPager.setCurrentItem(1, false);

        viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                super.onPageScrolled(position, positionOffset, positionOffsetPixels);

                if (position == 0) {
                    viewPager.setCurrentItem(5, false);
                }
                else if (position == 5) {
                    viewPager.setCurrentItem(1, false);
                }
            }
        });
    }
}

You'll obviously have to change the positions depending on the number of Fragments you have.

Bennington answered 12/2, 2023 at 17:34 Comment(0)
M
-1

Sorry for the delay, but I've seen the answers and I I was unable to hold, got to answer it too :).

On your adapter, on the method get count, proceed as follows:

 @Override
    public int getCount() {
        return myList.size() % Integer.MAX_VALUE; 
  }

That will work!

Monotype answered 6/4, 2014 at 16:28 Comment(1)
Why would this work? x % Integer.MAX_VALUE == x for any x < Integer.MAX_VALUEPeriosteum

© 2022 - 2024 — McMap. All rights reserved.