6

I have a FragmentStateAdapter that is attached to new ViewPager2. In the previous ViewPager implementation there were number of ways to implement endless/infinite scroll. How to do so in ViewPager2?

Thanks in advance.

Ali Yucel Akgul
  • 1,012
  • 7
  • 25
  • 49

4 Answers4

1

This is what I came up with:

class EndlessScrollAdapter internal constructor(
    fm: FragmentManager,
    lifeCycle: Lifecycle
) : FragmentStateAdapter(fm, lifeCycle) {

    private val items = mutableListOf<YourModel>()
    val firstElementPosition = Int.MAX_VALUE / 2

    fun updateList(list: List<YourModel>) {
        items.apply {
            clear()
            addAll(list)
        }
        notifyDataSetChanged()
    }

    override fun getItemCount(): Int = if (items.isNotEmpty()) Int.MAX_VALUE else 0

    override fun createFragment(position: Int): Fragment = YourPagerFragment(
        items[position.rem(items.size)])
}

In Activity or Fragment we need to call:

viewPager2.adapter = endlessScrollAdapter
endlessScrollAdapter.apply {
  updateList(yourModelList)
  viewPager2.setCurrentItem(this.firstElementPosition, false)
}

Literally it's not endless but from the user perspective it is, as he will never reach to the edge. The lenght of ViewPager2 is Int.MAX_VALUE, the start position is Int.MAX_VALUE/2 so user can scroll foward and backwards.

Da Artagnan
  • 829
  • 3
  • 13
  • 22
1

I am quite new to android and I passed the whole day reading all posts I could find about this problem. I also have a FragmentStateAdapter with 3 Fragments attached to a viewPager2.

My solution was to override the OnPageChangeCallback event. Here, I saved in 2 local variables the state and the currentPosition; then, in the onPageScrolled, I added a control to understand:

  1. if the swipe event started
  2. if the new position was equal to the current position. Observing the logs I noticed that they were the same only when I tried to scroll from first page to the left or from the last one to the right.
  3. if the page position was first or last

If all these conditions were true and I was on the first page, then I would set the next page to be the last and vice versa.

Here is my code. I used 0 and 2 because I have 3 Fragments.

viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback()
{
    private int myState;
    private int currentPosition;

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
    {
        if (myState == ViewPager2.SCROLL_STATE_DRAGGING && currentPosition == position && currentPosition == 0)
            viewPager2.setCurrentItem(2);
        else if (myState == ViewPager2.SCROLL_STATE_DRAGGING && currentPosition == position && currentPosition == 2)
            viewPager2.setCurrentItem(0);

        super.onPageScrolled(position, positionOffset, positionOffsetPixels);
    }

    @Override
    public void onPageSelected(int position)
    {
        currentPosition = position;

        super.onPageSelected(position);
    }

    @Override
    public void onPageScrollStateChanged(int state)
    {
        myState = state;

        super.onPageScrollStateChanged(state);
    }
});
  • This piece of code save my one day effort. All though I was looking for something else but using this logic we can restrict multiple onPageScrolled calling. – Subho Jan 17 '21 at 15:12
0

you should return first or last position of view_pager when scrolling finished.

see this issue - Changing ViewPager to enable infinite page scrolling

Nirel
  • 1,734
  • 1
  • 12
  • 24
HGH806
  • 49
  • 1
  • 5
  • Thanks a lot for your comment, but the link you've given is for ViewPager, I need for ViewPager2 adapter. (It's basically a recyclerview adapter) – Ali Yucel Akgul Nov 06 '19 at 22:11
0

Define a class extending ViewPager2.OnPageChangeCallback :

class ViewPager2PageChangeCallback(private val listener: (Int) -> Unit) :
        ViewPager2.OnPageChangeCallback() {

    override fun onPageSelected(position: Int) {
        super.onPageSelected(position)
        when (position) {
            0 -> listener.invoke(5). // 0th element is a fake element in your list. When 0th element is opened(from 1 to 0) automatically open last element(5th index)
            6 -> listener.invoke(1). // 6th element is a fake element in your list. When 6th element is opened(from 5 to 6) automatically open first element(1th index)
        }
    }
}

Then create an instance from it in your activity.

onCreate(){
    viewPager2PageChangeCallback = ViewPager2PageChangeCallback {
        viewPager2.post {
            viewPager2.setCurrentItem(it, false)
        }
    }
    viewPager2.registerOnPageChangeCallback(viewPager2PageChangeCallback)
}

Unregister from it when activity is destroyed.

override fun onDestroy() {
    super.onDestroy()
    viewPager2.unregisterOnPageChangeCallback(viewPager2PageChangeCallback)
}
oiyio
  • 3,108
  • 3
  • 34
  • 41