ViewPager.setCurrentItem only works w/ smoothScroll set to true
Asked Answered
C

1

7

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?

Cicily answered 26/10, 2016 at 20:19 Comment(3)
The first place I would look is if your FragmentPagerAdapter#getItem() is properly being called with the appropriate positions. It sounds like it's not loading the fragments appropriately for some reason. You should see it called two maybe three times. Once at the selected position, one before and one after.Cultivar
@Deev, I added entries to the logcat to test this idea. The result can be found at pastie.org/private/ybvvn5rwl1465haic6fvq. getItem is called 3 times when setCurrentItem(i, false) (i.e. the unexplained situation) is triggered. Notice that the log has getItem(1327) yet newInstance(27). This is because of the "infinite" viewer - 1327 % 52 = 27Cicily
@levengi I am not sure what the issue is then. It seems like the example may have been with the assumption that smooth scroll is enabled.Cultivar
B
4

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!

Besprent answered 12/11, 2016 at 23:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.