Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

Я не могу действительно ответить ПОЧЕМУ именно это происходит, но если вы задерживаете вызов 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, чтобы уменьшить этот faux-fix-provider и...

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


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

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

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

Ответ 2

У меня была аналогичная проблема в OnCreate моей деятельности. Адаптер был настроен с правильным подсчетом, а я Примените 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);
        }
    });

Ответ 3

Я нашел очень простой способ:

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

Ответ 4

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

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

Я установил NUM_PAGES ошибкой только 1.

Ответ 5

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

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

notifyDataSetChanged()
setCurrentItem()

Ответ 6

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

Моя проблема заключалась в том, что я хочу иметь

notifyDataSetChanged

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

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 в конце состоит в том, что в API есть ошибка орфографии, которая была исправлена ​​после API 16, поэтому следует прочитать remove Вкл GlobalLayoutListener вместо removeGlobal О LayoutListener

Это, кажется, охватывает все случаи для меня.

Ответ 7

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

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

viewPager.setCurrentItem(idx);

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

Ответ 8

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

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

Ответ 9

Решение (в Kotlin с ViewModel и т.д.) Для тех, кто пытается установить текущий элемент в onCreate из Activity без хакерских Runnable "решений":

class MyActivity : AppCompatActivity() {
    lateinit var mAdapter: MyAdapter
    lateinit var mPager: ViewPager
    // ...

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.fragment_pager)
        // ...
        mainViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
        mAdapter = MyAdapter(supportFragmentManager)
        mPager = findViewById(R.id.pager)

        mainViewModel.someData.observe(this, Observer { items ->
            items?.let {
                // first give the data to the adapter
                // this is where the notifyDataSetChanged() happens
                mAdapter.setItems(it) 
                mPager.adapter = mAdapter // assign adapter to pager
                mPager.currentItem = idx // finally set the current page
            }
        })

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

Для полноты вы обычно реализуете setItems() адаптера (в данном случае FragmentStatePagerAdapter) следующим образом:

internal fun setItems(items: List<Item>) {
    this.items = items
    notifyDataSetChanged()
}

Ответ 10

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

@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();
}

Ответ 11

Это проблема жизненного цикла, о чем здесь свидетельствуют несколько плакатов. Тем не менее, я считаю, что решения с публикацией 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); 
    }


}

Ответ 12

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

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

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

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

Ответ 13

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

Ответ 14

Я работал над этой проблемой в течение одной недели и понял, что эта проблема возникает из-за того, что я использовал контекст домашней активности для просмотра фрагментов пейджера, и мы можем использовать контекст во фрагменте только после того, как он привязан к действию.
Когда пейджер представления создается, действие прикрепляется только к первой (0) и второй (1) странице. Когда вы открываете вторую страницу, третья страница прикрепляется и так далее! Когда вы используете метод setCorrentItem(), а аргумент больше 1, он хочет открыть эту страницу, прежде чем она будет прикреплена, поэтому контекст во фрагменте этой страницы будет нулевым, и приложение будет аварийно завершено! Вот почему, когда вы задерживаете setCorrentItem(), это работает! Сначала он прикрепляется, а затем откроет страницу...

Ответ 15

Вам нужно вызвать pager.setCurrentItem(activePage) сразу после pager.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);
}