Android ViewPager setCurrentItem not working after onResume

62,926

Solution 1

i can't really answer WHY exactly this happens, but if you delay the setCurrentItem call for a few milliseconds it should work. My guess is that because during onResume there hasn't been a rendering pass yet, and the ViewPager needs one or something like that.

private ViewPager viewPager;

@Override
public void onResume() {
    final int pos = 3;
    viewPager.postDelayed(new Runnable() {

        @Override
        public void run() {
            viewPager.setCurrentItem(pos);
        }
    }, 100);
}

UPDATE: story time

so today i had the problem that the viewpager ignored my setCurrentItem action, and i searched stackoverflow for a solution. i found someone with the same problem and a fix; i implemented the fix and it didn't work. whoa! back to stackoverflow to downvote that faux-fix-provider, and ...

it was me. i implemented my own faulty non-fix, which i came up with the first time i stumbled over the problem (and which was later forgotten). i'll now have to downvote myself for providing bad information.


the reason my initial "fix" worked was not because of of a "rendering pass"; the problem was that the pager's content was controlled by a spinner. both the spinners and the pagers state were restored onResume, and because of this the spinners onItemSelected listener was called during the next event propagation cycle, which did repopulate the viewpager - this time using a different default value.
removing and resetting the listener during the initial state restoration fixed the issue.

the fix above kind-of worked the first time, because it set the pagers current position after the onItemSelected event fired. later, it ceased to work for some reason (probably the app became too slow - in my implementation i didn't use 100ms, but 10ms). i then removed the postDelayed in a cleanup cycle, because it didn't change the already faulty behaviour.

update 2: i can't downvote my own post. i assume, honorable seppuku is the only option left.

Solution 2

I had a similar issue in the OnCreate of my Activity. The adapter was set up with the correct count and I applied setCurrentItem after setting the adapter to the ViewPager however is would return index out of bounds. I think the ViewPager had not loaded all my Fragments at the point i set the current item. By posting a runnable on the ViewPager i was able to work around this. Here is an example with a little bit of context.

    // Locate the viewpager in activity_main.xml
    final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);

    // Set the ViewPagerAdapter into ViewPager
    viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));

    viewPager.setOffscreenPageLimit(2);

    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setCurrentItem(ViewPagerAdapter.CENTER_PAGE);
        }
    });

Solution 3

I found a very simple workaround for this:

    if (mViewPager.getAdapter() != null)
        mViewPager.setAdapter(null);
    mViewPager.setAdapter(mPagerAdapter);
    mViewPager.setCurrentItem(desiredPos);

And, if that doesn't work, you can put it in a handler, but there's no need for a timed delay:

        new Handler().post(new Runnable() {
            @Override
            public void run() {
                mViewPager.setCurrentItem(desiredPos);
            }
        });

Solution 4

ViewTreeObserver can be used to avoid a static delay.

Kotlin:

Feel free to use Kotlin extension as a concise option.

view_pager.doOnPreDraw {
    view_pager.currentItem = 1
}

Please, make sure you have a gradle dependency: implementation 'androidx.core:core-ktx:1.3.2' or above

Java

OneShotPreDrawListener.add(view_pager, () -> view_pager.currentItem = 1);

Solution 5

I had similar bug in the code, the problem was that I was setting the position before changing the data.

The solution was simply to set the position afterwards and notify the data changed

notifyDataSetChanged()
setCurrentItem()
Share:
62,926
Maciej Boguta
Author by

Maciej Boguta

Updated on July 09, 2022

Comments

  • Maciej Boguta
    Maciej Boguta almost 2 years

    I've got this strange issue, ViewPager's setCurrentItem(position, false) works perfectly fine, then im switching to another activity, and after I'm back to the first activity, the ViewPager always ends up on the first item. Even though I've added setCurrentItem to onResume method it still ignores it. It's not even throwing any exception when I'm trying to set item to out of bounds index. Though later on when I call this method, when the button "next" is tapped, it works like expected. Checked my code 10 times for any possible calls to setCurrentItem(0) or something but it's just not there at all.

  • Maciej Boguta
    Maciej Boguta over 10 years
    The thing is im not restoring instance state, its just onStart and onResume method called. Ive got the position stored properly, aswell i looked into my Viewpager object at debug mode and it has mCurItem set properly, yet its still showing the first element
  • Szymon
    Szymon over 10 years
    I can't remember exactly but I think I has a similar problem and I used the code above. But maybe not exactly the same.
  • Andrew Orobator
    Andrew Orobator almost 10 years
    What do you mean the pager's content was controlled by a spinner? Are you talking about the ViewPager's underlying implementation, or saying the content of each page in the viewpager contained a spinner? And I'm still having this bug even if the first thing I do in onResume() is call super.onResume(). By doing that, view states should be restored, and I shouldn't have to deal with what you described above
  • stefs
    stefs almost 10 years
    no, i mean: depending on the selected item in the spinner, the viewpager (i.e. the adapter) was populated with different items. in onResume both the spinner and the pager were re-initialized and the spinner's onItemSelected callback for updating the viewpager "overwrote" the correctly set viewpager position. it was my own fault, the viewpager worked correctly.
  • Andrew Orobator
    Andrew Orobator almost 10 years
    I see. I had the same issue but quite a different root cause. My ViewPager was hosted by a fragment and I was using getFragmentManager() instead of getChildFragmentManager() when passing a FragmentManager to my FragmetStatePagerAdapter
  • Crawler
    Crawler over 9 years
    Applying rendering delay. Felt awkward when read your answer but it did solve my problem...
  • stefs
    stefs over 9 years
    @Crawler: it's a trap! don't do it. it might seem to work at first, and gets unpredictable results a couple of months later when the code had time to stew a bit; then it turns into debugging hell and unpredictable 1-in-a-100 odd-behaviours that leads to testers writing bug reports like "X fails sometimes for no reason".
  • Crawler
    Crawler over 9 years
    @stefs thanks for the warning.....But I now again stuck in problem.... Do you have any lead in solvin this issue?
  • stefs
    stefs over 9 years
    as i said, for me the problem was something entirely different, i.e. calling setCurrentItem() twice. i guess the same thing applies in your case too.
  • codebased
    codebased almost 9 years
    @stefs that solvs the problem but it is not bringing good experience for the user at all with animation. There must be some better way to do that!
  • Dani
    Dani almost 9 years
    That was my problem too... ty!
  • Lazy
    Lazy over 8 years
    Thank you, second approach fixed my problem.
  • user3560827
    user3560827 almost 8 years
    @stefs 2:31am in the morning and finally your answer solved my problem by post delaying before setting the viewpager item position as well.
  • stefs
    stefs almost 8 years
    @user3560827 i'm sure you're either calling setCurrentItem again afterwards or assign a new adapter, thanks to some lifecycle shenanigans or maybe after some async operation. DO NOT depend on postDelayed, it's a hack and practically technical debt. your problem lies elsewhere!
  • Chisko
    Chisko almost 7 years
    @TylerPfaff this just worked for me too. Apparently, the ViewPager ignores "early-into-the-activity" instructions because it's not yet aware of what to render
  • Martin Price
    Martin Price over 6 years
    @TylerPfaff I believe it has something to do with when the ViewPager and its children are measured. It appears to ignore setCurrentItem until the has completed. The runnable posted on the ViewPager is guaranteed to execute after that measure.
  • Variag
    Variag almost 6 years
    In my case, I moved all the init lines (like setting adapter, listeners, etc.) from onResume() to onCreateView() and it worked
  • MindRoasterMir
    MindRoasterMir about 5 years
    You are the best. This worked at last. i was getting data from a volley json request. the views were initially empty. I tried a lot of solutions but none of them worked. but your worked like magic. thanks.
  • Girish
    Girish about 5 years
    @stefs yes correct Calling setCurrentItem() twice doesn't work..good catch
  • MJ Studio
    MJ Studio over 4 years
    This is happend in ViewPager2 also;;;;;;;;;;;; wtf
  • Jonathan
    Jonathan over 4 years
    This isn't working for me, and neither are any of the other solutions mentioned on this thread. I'm stumped, what a strange problem.
  • Rohan Arora
    Rohan Arora about 3 years
    Thank you @stefs you are saved my day, this really worked for me when I was switching fragments and trying to set the position in onCreate. I guess it needs time to set all fragments etc
  • Gilberto Ibarra
    Gilberto Ibarra about 3 years
    This continue happening on viewPager2 . it sucks
  • Braian Coronel
    Braian Coronel about 3 years
    I remember very well that this had worked for me brother. Try to debug the critical points. Then it evaluates how the adapter is initialized if the observable has data, and how the adapter sets the new data when the observable is updated
  • Henrique Vasconcellos
    Henrique Vasconcellos over 2 years
    great answer - only solution that worked in viewpager2!
  • Ilya E
    Ilya E over 2 years
    wow, that story deserves a netflix adaptation
  • Asif Ullah
    Asif Ullah almost 2 years
    perfect. thanks for the detailed story. saved my day.