Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewPager.setCurrentItem only works w/ smoothScroll set to true

I have created an infinite extension of FragmentPagerAdapter (there are examples on how to achieve this on this site). This allows me to iterate through 50 (arbitrary number) sets of 52 fragments (one per week) thereby giving an infinite feel of fragments to the user.

When scrolling/jumping between fragments by calling ViewPager.setCurrentItem, there are two scenarios that I see:

  1. Jumping only one fragment either way - all is ok. This is presumably due to the code that specializes this use-case in ViewPager.setCurrentItemInternal (look for the comment beginning with the words We are doing a jump by more than one page)
  2. Jumping by more than one fragment, the new fragment is shown properly on the screen only if setCurrentItem is called when smoothScroll is set to true (i.e. setCurrentItem(i, true)); otherwise there is a blank screen

From what I can see, this is probably because ViewPager.scrollToItem has the following code in it:

if (smoothScroll) {
    smoothScrollTo(destX, 0, velocity);
    if (dispatchSelected) {
        dispatchOnPageSelected(item);
    }
} else {
    if (dispatchSelected) {
        dispatchOnPageSelected(item);
    }
    completeScroll(false);
    scrollTo(destX, 0);
    pageScrolled(destX);
}

This is the point where I am out of my depth. Why would this if/else cause the phenomena that I am experiencing?

like image 460
levengli Avatar asked Oct 26 '16 20:10

levengli


1 Answers

The explanation is pretty simple - ViewPager doesn't keep all your fragments' states, cause it will be a disaster for performance if it would keep all states active.

There is an setOffscreenPageLimit method exists exactly for this case. Its purpose is to define how many fragments' states the ViewPager should keep to the left and to the right from the current one. For more background check official documentation: https://developer.android.com/reference/android/support/v4/view/ViewPager.html#setOffscreenPageLimit(int)

Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.

This is offered as an optimization. If you know in advance the number of pages you will need to support or have lazy-loading mechanisms in place on your pages, tweaking this setting can have benefits in perceived smoothness of paging animations and interaction. If you have a small number of pages (3-4) that you can keep active all at once, less time will be spent in layout for newly created view subtrees as the user pages back and forth.

You should keep this limit low, especially if your pages have complex layouts. This setting defaults to 1.

So as we can see the default limit is 1 - that's is the answer why jumping only one fragment works perfectly. And it's also the answer for smooth scrolling case - when you want to set new current item with smooth scrolling it means you need to scroll through the all of fragments one by one - and here is the case when default limit 1 works.

So in your case you can try to setOffscreenPageLimit(52), and then setCurrentItem(50) should work as expected. It's not recommended, just do it to see the behavior. If you have some difficult job in your fragment (like loading some data from network) then it would a big delay on startup, cause all fragments would load at once.

Hope that helps!

like image 159
romtsn Avatar answered Oct 15 '22 01:10

romtsn