Android ViewPager setCurrentItem не работает после onResume

Ive получил эту странную проблему, setCurrentItem ViewPager (position, false) работает отлично, затем im переключается на другое действие, а после im возвращается к первому действию, ViewPager всегда заканчивается на первом элементе. Несмотря на то, что ive добавил setCurrentItem в метод onResume, он по-прежнему игнорирует его. Его даже не бросает никакого исключения, когда я пытаюсь установить элемент вне индекса границ. Хотя позже, когда я вызываю этот метод, когда нажимается кнопка "Далее", он работает так ожидаемый. Проверил мой код 10 раз для любых возможных вызовов setCurrentItem (0) или smth, но его просто нет вообще.

13 ответов


Я не могу ответить, почему именно это происходит, но если вы откладываете вызов setCurrentItem на несколько миллисекунд он должен работать. Я предполагаю, что это потому, что во время onResume еще не было прохода рендеринга, и ViewPager нуждается в одном или чем-то подобном.

private ViewPager viewPager;

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

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

обновление: время истории

Итак, сегодня у меня была проблема, что viewpager проигнорировал мое действие setCurrentItem, и я искал stackoverflow для решения. я нашел кое-кого с та же проблема и исправление; я реализовал исправление, и оно не сработало. ого! вернемся к stackoverflow, чтобы downvote, что faux-fix-provider, и ...

это был я. я реализовал свой собственный неисправный non-fix, который я придумал в первый раз, когда я наткнулся на проблему (и который позже был забыт). теперь мне придется понизить голос за предоставление плохой информации.


причина моего первоначального "исправления" работала не из-за" прохода рендеринга"; проблема заключалась в том, что содержимое пейджера контролировалось спиннером. как блесны, так и состояние пейджеров были восстановлены onResume, и из - за этого прослушиватель spinners onItemSelected был вызван во время следующего цикла распространения событий, который действительно перенаселил viewpager-на этот раз используя другое значение по умолчанию.
удаление и сброс прослушивателя во время восстановления исходного состояния Исправлена проблема.

исправление выше вида работало в первый раз, потому что оно установило ток пейджеров позиция после запущено событие onItemSelected. позже он почему - то перестал работать (возможно, приложение стало слишком медленным-в моей реализации я использовал не 100ms, а 10ms). затем я удалил postDelayed в цикле очистки, потому что он не изменил уже неисправное поведение.

обновление 2: Я не могу понизить свой собственный пост. полагаю, достопочтенный сеппуку-единственный оставшийся вариант.


У меня была аналогичная проблема в OnCreate моей деятельности. Адаптер был настроен с правильным количеством и I применяется setCurrentItem после установки адаптера в ViewPager, однако, вернет индекс за пределы. Я думаю, что ViewPager не загрузил все мои фрагменты в тот момент, когда я установил текущий элемент. Разместив runnable на ViewPager, я смог обойти это. Вот пример с небольшим контекстом.

    // 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);
        }
    });

Я нашел очень простой обходной путь для этого:

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

и, если это не работает, вы можете поместить его в обработчик, но нет необходимости в временной задержке:

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

у меня такая же проблема и я редактирую

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

Я NUM_PAGES быть ошибкой только 1.


У меня была аналогичная ошибка в коде, проблема заключалась в том, что я устанавливал позицию перед изменением данных.

решение было просто установить позицию после этого и уведомить об изменении данных

notifyDataSetChanged()
setCurrentItem()

для меня это работало настройка текущего элемента после установки адаптера

viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));

viewPager.setCurrentItem(idx);

pagerSlidingTabStrip.setViewPager(viewPager);// assign viewpager to tabs

я использовал метод post (), описанный здесь, и, конечно же, он отлично работал в некоторых сценариях, но поскольку мои данные поступают с сервера, это не был святой Грааль.

моя проблема была в том, что я хочу иметь

notifyDataSetChanged

вызывается в произвольное время, а затем переключает вкладки на моем viewPager. Поэтому сразу после вызова notify у меня есть это

ViewUtilities.waitForLayout(myViewPager, new Runnable() {
    @Override
    public void run() {
        myViewPager.setCurrentItem(tabIndex , false);
    }
});

и

public final class ViewUtilities {
    public static void waitForLayout(final View view, final Runnable runnable) {
        view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            //noinspection deprecation
                view.getViewTreeObserver().removeGlobalOnLayoutListener(this);

                runnable.run();
            }
        });
    }
}

забавный факт: / / noinspection deprecation в конце это потому, что есть орфографическая ошибка в API, которая была исправлена после API 16, так что следует прочитать remove OnGlobalLayoutListener вместо removeGlobal OnLayoutListener

это, кажется, охватывает все дела для меня.


Я сделал это таким образом, чтобы восстановить текущего элемента:

@Override
protected void onSaveInstanceState(Bundle outState) {

    if (mViewPager != null) {
        outState.putInt(STATE_PAGE_NO, mViewPager.getCurrentItem());
    }

    super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {

    if (savedInstanceState != null) {
        mCurrentPage = savedInstanceState.getInt(STATE_PAGE_NO, 0);
    }

    super.onRestoreInstanceState(savedInstanceState);
}

@Override
protected void onRestart() {
    mViewPager.setCurrentItem(mCurrentPage);
         super.onRestart();
}

это проблема жизненного цикла, как указано в нескольких плакатах здесь. Тем не менее, я нахожу решения с публикацией Runnable быть непредсказуемым и наверное ошибок. Это похоже на способ игнорировать проблему, отправляя ее в будущее.

Я не говорю, что это лучшее решение, но это, безусловно, работает без использования Runnable. Я держу отдельное целое число внутри Fragment что есть ViewPager. это целое число будет содержать страницу мы хотите установить в качестве текущей страницы, когда onResume называется следующий. значение целого числа может быть установлено в любой точке и, таким образом, может быть установлено перед FragmentTransaction или при возобновлении деятельности. Также обратите внимание, что все члены настроены в onResume(), не onCreateView().

public class MyFragment extends Fragment
{
    private ViewPager           mViewPager;
    private MyPagerAdapter      mAdapter;
    private TabLayout           mTabLayout;
    private int                 mCurrentItem = 0; // Used to keep the page we want to set in onResume().

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.my_layout, container, false);
        mViewPager = (ViewPager) view.findViewById(R.id.my_viewpager);
        mTabLayout = (TabLayout) view.findViewById(R.id.my_tablayout);
        return view;
    }

    @Override
    public void onResume()
    {
        super.onResume();

        MyActivity myActivity = (MyActivity) getActivity();
        myActivity.getSupportActionBar().setTitle(getString(R.string.my_title));

        mAdapter = new MyPagerAdapter(getChildFragmentManager(), myActivity);
        mViewPager.setAdapter(mAdapter);
        mViewPager.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
        mViewPager.setCurrentItem(mCurrentItem); // <-- Note the use of mCurrentItem here!
        mTabLayout.setupWithViewPager(mViewPager);
    }

    /**
     * Call this at any point before needed, for example before performing a FragmentTransaction.
     */    
    public void setCurrentItem(int currentItem)
    {
        mCurrentItem = currentItem;

        // This should be called in cases where onResume() is not called later,
        // for example if you only want to change the page in the ViewPager
        // when clicking a Button or whatever. Just omit if not needed.
        mViewPager.setCurrentItem(mCurrentItem); 
    }


}

к тому времени, как я позвоню setCurrentItem() представление будет воссоздано. Поэтому на самом деле я призываю setCurrentItem() для viewpager, а затем система вызывает onCreateView() и, следовательно, создает новый viewpager.

это причина для меня, почему я не вижу никаких изменений. И именно по этой причине a postDelayed() может помочь.

теоретическое решение: отложить setCurrentItem() вызов до тех пор, пока представление не будет воссоздано.

практические решение: Я понятия не имею о стабильном и простом решении. Мы должны иметь возможность проверить, собирается ли класс воссоздать его представление, и если это так, отложите вызов setCurrentItem() до конца onCreateView()


какой-то парень написал на форумах здесь. https://code.i-harness.com/en/q/126bff9 работал на меня

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

ЧИСТЫЙ И ПРОСТОЙ Нет необходимости добавлять метод post только setCurrentItem после вызова notifyDataSetChanged ().


вам нужно вызвать пейджер.setCurrentItem(activePage) сразу после пейджера.setAdapter (buildAdapter ())

@Override
public void onResume() {
    if (pager.getAdapter() != null) {
        activePage=pager.getCurrentItem();
        Log.w(getClass().getSimpleName(), "pager.getAdapter()!=null");
        pager.setAdapter(null);

    }

    pager.setAdapter(buildAdapter());            
    pager.setCurrentItem(activePage);
}