Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FragmentStatePagerAdapter stopped working after updating to ADT 22?

Tags:

android

I have been using a FragmentStatePagerAdapter in one of my apps for many months, and it has worked flawlessly. I recently switched to IntelliJ IDEA and updated to ADT 22 and now the Adapter causes the app to force close when the app tries to destroy tabs and create new ones.

When working correctly, the Activity will load a series of tabs. The user can swipe between them. All of this still works correctly.

The app should destroy these tabs and create new ones when the user chooses a button in the menu. Normally, this works just fine but now it has suddenly stopped working. Here is the code:

/** TabsAdapter - Make Tabs Swipe-Able, Handle Fragments */
public static class TabsAdapter extends FragmentStatePagerAdapter 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;
        }
    } // end TabInfo

    public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
        super(activity.getSupportFragmentManager());
        mContext = activity;
        mActionBar = activity.getSupportActionBar();
        mViewPager = pager;
        mViewPager.setAdapter(this);
        mViewPager.setOnPageChangeListener(this);
    } // end TabsAdapter

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

    public void removeTab(ActionBar.Tab tab) {
        mTabs.remove(tab.getTag());
        mActionBar.removeTab(tab);
        notifyDataSetChanged();
    }

    @Override
    public int getItemPosition(Object object) {
        return TabsAdapter.POSITION_NONE;
    }

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

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

    @Override
    public void onPageScrolled(int position, float positionOffest, int postionOffsetPixels){

    } // end onPageScrolled

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

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

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

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

    @Override
    public void onPageScrollStateChanged(int arg0) {}

} // end TabsAdapter

During onCreate(), I call a method, let's call it CreateTabs1(), and it successfully builds the tabs and the user can swipe. It is only when the pager tries to destroy the tabs and create new ones that it now fails all of the sudden, even if I call CreateTabs1() again. Again, this always worked flawlessly before.

Here is the log:

05-25 23:41:24.857: ERROR/AndroidRuntime(27290): FATAL EXCEPTION: main
    java.lang.IllegalStateException: The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: 11, found: 10 Pager id: com.######:id/pager Pager class: class android.support.v4.view.ViewPager Problematic adapter: class com.#####.######.#####$TabsAdapter
    at android.support.v4.view.ViewPager.populate(ViewPager.java:959)
    at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:548)
    at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:507)
    at android.support.v4.view.ViewPager.setCurrentItem(ViewPager.java:488)
    at com.#####.#####.#####$TabsAdapter.onTabSelected(#####.java:215)
    at com.actionbarsherlock.internal.app.ActionBarWrapper$TabWrapper.onTabSelected(ActionBarWrapper.java:356)
    at com.android.internal.app.ActionBarImpl.selectTab(ActionBarImpl.java:570)
    at com.android.internal.app.ActionBarImpl.removeTabAt(ActionBarImpl.java:544)
    at com.android.internal.app.ActionBarImpl.removeTab(ActionBarImpl.java:520)
    at com.actionbarsherlock.internal.app.ActionBarWrapper.removeTab(ActionBarWrapper.java:409)
    at com.#####.#####.#####$TabsAdapter.removeTab(#####.java:181)
    at com.#####.#####.#####.tabDrop(#####.java:381)
    at com.#####.#####.#####.onOptionsItemSelected(#####.java:459)
    at android.support.v4.app.Watson.onMenuItemSelected(Watson.java:118)
    at com.actionbarsherlock.ActionBarSherlock.callbackOptionsItemSelected(ActionBarSherlock.java:603)
    at com.actionbarsherlock.internal.ActionBarSherlockNative.dispatchOptionsItemSelected(ActionBarSherlockNative.java:93)
    at com.actionbarsherlock.app.SherlockFragmentActivity.onMenuItemSelected(SherlockFragmentActivity.java:205)
    at com.android.internal.policy.impl.PhoneWindow.onMenuItemSelected(PhoneWindow.java:980)
    at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:735)
    at com.android.internal.view.menu.SubMenuBuilder.dispatchMenuItemSelected(SubMenuBuilder.java:81)
    at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:149)
    at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:874)
    at com.android.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:156)
    at android.widget.AdapterView.performItemClick(AdapterView.java:298)
    at android.widget.AbsListView.performItemClick(AbsListView.java:1100)
    at android.widget.AbsListView$PerformClick.run(AbsListView.java:2749)
    at android.widget.AbsListView$1.run(AbsListView.java:3423)
    at android.os.Handler.handleCallback(Handler.java:725)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:5041)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
    at dalvik.system.NativeStart.main(Native Method)
like image 489
Ninjalan Avatar asked May 26 '13 04:05

Ninjalan


1 Answers

After ADT 22 the PagerAdapter has gotten very strict about calling notifyDataSetChanged() before calling getCount(). It evidently keeps track of what it thinks the count should be and if this is not the same as what getCount() returns it throws this exception. So the solution is simply to call notifyDataSetChanged() on the adapter every time the size of the data changes.

In my case I have a background task that is creating an array of data. After the array is filled I call notifyDataSetChanged() in onPostExecute(). When I had a path in onPostExecute() that did not call notifyDataSetChanged() I was getting the exception.

like image 106
charles young Avatar answered Oct 16 '22 18:10

charles young