IllegalStateException: PagerAdapter приложения изменил содержимое адаптера без вызова PagerAdapter#notifyDataSetChanged

Я использую пример ViewPager с ActionBar вкладками, взятый из документации Android здесь .

К сожалению, как только я вызываю метод addTab, приложение вылетает со следующим исключением:

IllegalStateException: PagerAdapter приложения изменил содержимое адаптера без вызова PagerAdapter#notifyDataSetChanged! Ожидаемое количество элементов адаптера 0, найдено 1.

Это код FragmentPagerAdapter:

public static class TabsAdapter extends FragmentPagerAdapter
            implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
        private final Context mContext;
        private final ActionBar mActionBar;
        private final ViewPager mViewPager;
        private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();

        static final class TabInfo {
            private final Class<?> clss;
            private final Bundle args;

            TabInfo(Class<?> _class, Bundle _args) {
                clss = _class;
                args = _args;
            }
        }

        public TabsAdapter(Activity activity, ViewPager pager) {
            super(activity.getFragmentManager());
            mContext = activity;
            mActionBar = activity.getActionBar();
            mViewPager = pager;
            mViewPager.setAdapter(this);
            mViewPager.setOnPageChangeListener(this);
        }

        public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
            TabInfo info = new TabInfo(clss, args);
            tab.setTag(info);
            tab.setTabListener(this);
            mTabs.add(info);
            mActionBar.addTab(tab);
            notifyDataSetChanged();
        }

        @Override
        public int getCount() {
            return mTabs.size();
        }

        @Override
        public Fragment getItem(int position) {
            TabInfo info = mTabs.get(position);
            return Fragment.instantiate(mContext, info.clss.getName(), info.args);
        }

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }

        @Override
        public void onPageSelected(int position) {
            mActionBar.setSelectedNavigationItem(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
        }

        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            Object tag = tab.getTag();
            for (int i=0; i<mTabs.size(); i++) {
                if (mTabs.get(i) == tag) {
                    mViewPager.setCurrentItem(i);
                }
            }
        }

        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        }

        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {
        }
    }
}

Я не изменяю свой адаптер ни в какой другой части своего кода, и я вызываю метод addTab из основного потока, метод addTab заканчивается вызовом notifyDataSetChanged. Как рекомендует делать документация:

PagerAdapter поддерживает изменения набора данных. Изменения набора данных должны происходить в основном потоке и должны заканчиваться вызовом notifyDataSetChanged() аналогично адаптерам AdapterView, производным от BaseAdapter.


person Zagorax    schedule 08.04.2014    source источник


Ответы (10)


Мне было трудно заставить мой ViewPager работать. В итоге кажется, что пример в документации неправильный.

Метод addTab должен быть следующим:

public void addTab(Tab tab, Class<?> clss, Bundle args) {
        TabInfo info = new TabInfo(clss, args);
        tab.setTag(info);
        tab.setTabListener(this);
        mTabs.add(info);
        notifyDataSetChanged();
        mActionBar.addTab(tab);
    }

Обратите внимание на порядок последних трех операций. В исходном примере notifyDataSetChanged вызывалась после функции mActionBar.addTab.

К сожалению, как только вы вызываете addTab на ActionBar, автоматически выбирается первая добавленная вами вкладка. Из-за этого запускается событие onTabSelected, и при попытке получить страницу оно выдает IllegalStateException, потому что замечает несоответствие между ожидаемым количеством элементов и фактическим.

person Zagorax    schedule 08.04.2014
comment
Я искал это некоторое время. Я единственный, кто находит странным, что ошибка читается так, как будто я не вызываю notifyDataSetChanged(), когда на самом деле мне нужно просто выполнить действие (setCurrentItem в моем случае) после notifyDataSetChanged()? - person wizurd; 06.09.2014

Я исправил это, вызвав notifyDataSetChanged() один раз и непосредственно перед следующим вызовом getCount():

private boolean doNotifyDataSetChangedOnce = false;

@Override
public int getCount() {

  if (doNotifyDataSetChangedOnce) {
    doNotifyDataSetChangedOnce = false;
    notifyDataSetChanged();
  }

  return actionBar.getTabCount();

}

private void addTab(String text) {

  doNotifyDataSetChangedOnce = true;

  Tab tab = actionBar.newTab();
  tab.setText(text);
  tab.setTabListener(this);
  actionBar.addTab(tab);

}

private void removeTab(int position) {

  doNotifyDataSetChangedOnce = true;

  actionBar.removeTabAt(position);

}
person almisoft    schedule 26.09.2014

Я получал эту ошибку, как и вы, ссылаясь на вкладки в getCount():

@Override
    public int getCount() {
        return mTabs.size();
    }

При создании экземпляра это должно быть либо передано как отдельная переменная, либо пейджер должен быть настроен после заполнения/изменения коллекции.

person Daniel Wilson    schedule 02.10.2015

попробуй это. Я работал

protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            System.out.println("Asyncrona onPostExecute");
            /*::::::::::::::::::: Guardo los cambios  ::::::::::::::*/
            //menuTabs = menuTabs2;
            viewPager = (ViewPager) rootView.findViewById(R.id.viewPager_menu);
            costumAdapter = new CostumAdapter2(getActivity().getSupportFragmentManager());
            costumAdapter.notifyDataSetChanged();

            viewPager.setAdapter(costumAdapter);
            tabLayout = (TabLayout) rootView.findViewById(R.id.tablayout_menu);
            tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
                @Override
                public void onTabSelected(TabLayout.Tab tab) {
                    System.out.println(" La Seleccionado: " + tab.getText()+", POSICION: "+tab.getPosition());
                    viewPager.setCurrentItem(tab.getPosition());
                }

                @Override
                public void onTabUnselected(TabLayout.Tab tab) {
                    viewPager.setCurrentItem(tab.getPosition());
                }

                @Override
                public void onTabReselected(TabLayout.Tab tab) {
                    viewPager.setCurrentItem(tab.getPosition());
                }

            });
            viewPager.setAdapter(costumAdapter);
            tabLayout.setupWithViewPager(viewPager);
            loading.closeMessage();

        }
person Isa M    schedule 20.04.2017

Моя проблема была не совсем такой же, но в похожем контексте.

У меня есть AlertDialog, который отображается в макете с ViewPager и использует AppCompatActivity. Я получал исключение IllegalStateException и сбой при повороте экрана.

Благодаря ответам @almisoft и @Zagorax я попытался позвонить notifyDataSetChanged(); прямо перед тем, как показать диалоговое окно, и проблема, похоже, исчезла.

person Gene Bo    schedule 12.11.2015

У меня была такая же проблема в адаптере FragmentStatePager. в onResume() При повторной инициализации адаптера и ViewPager будет показана та же ошибка IllegalStateException. Решение:

При добавлении вкладки addtab выберите вкладку, которая не будет выбрана, установив для параметров setSelected значение false.

 actionBar.addTab(tab,false);

Я думаю, что исключение появилось из-за переопределения метода onTabSelected(), как показано ниже.

 viewPager.setCurrentItem(tab.getPosition());

Поэтому при вызове addTab() он не распознал, к какому viewPager и адаптеру добавить вкладку.

person Amt87    schedule 24.08.2015

добавьте эту строку в файл адаптера, где вызывается метод public Object instanceItem (контейнер ViewGroup, int position)

((ViewPager) контейнер).addView(представление); обратный вид;

person Dhaval Barot    schedule 03.05.2018

Убедитесь, что значения в setOffscreenPageLimit и getCount совпадают, иначе вы получите эту ошибку:

    mViewPager.setOffscreenPageLimit(MAX_TABS);

    public class SectionsPagerAdapter extends FragmentPagerAdapter {
    ...
    @Override
    public int getCount() {
        return MAX_TABS;
    }
person live-love    schedule 07.08.2018

Вызов Viewpager.setAdapter(адаптер); после добавления элемента в список массивов.

person Vibhu Vikram Singh    schedule 11.06.2019

Еще одна вещь, которую нужно проверить, это адаптер, для которого он выдает этот список источников проверки ошибок, получает ссылку из другого списка или нет.

Возможно, на ваш список адаптеров ссылаются из другого списка, и в этом списке были обновлены и добавлены некоторые элементы.

person Ghodasara Bhaumik    schedule 27.10.2020