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