FragmentPagerAdapter is not removing items (Fragments) correctly

I got same problem,and my solution was overring the method "destroyItem" as following.

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    FragmentManager manager = ((Fragment)object).getFragmentManager();
    FragmentTransaction trans = manager.beginTransaction();
    trans.remove((Fragment)object);
    trans.commit();
}

It's work for me,does anybody have another solutions?

Updated:

I found those code made Fragment removed unnecessary,so I added a condition to avoid it.

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    if (position >= getCount()) {
        FragmentManager manager = ((Fragment) object).getFragmentManager();
        FragmentTransaction trans = manager.beginTransaction();
        trans.remove((Fragment) object);
        trans.commit();
    }
}

Updated this post and included my solution (if someone can improve let me know)

Ok i've now solved my problem in a hackish way, but yeah it's working ;). If someone can improve my solution please let me know. For my new solution i now use a CustomFragmentStatePagerAdapter but it doesn't save the state like it should and stores all the Fragments in a list. This can cause a memory problem if the user has more than 50 fragments, like the normal FragmentPagerAdapter does. It would be great if someone can add the State-thing back to my solution without removing my fixes. Thanks.

So here's my CustomFragmentStatePagerAdapter.java

package com.tundem.webLab.Adapter;

import java.util.ArrayList;

import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
    private static final String TAG = "FragmentStatePagerAdapter";
    private static final boolean DEBUG = false;

    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction = null;

    public ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
    public ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
    private Fragment mCurrentPrimaryItem = null;

    public CustomFragmentStatePagerAdapter(FragmentManager fm) {
        mFragmentManager = fm;
    }

    /**
     * Return the Fragment associated with a specified position.
     */
    public abstract Fragment getItem(int position);

    @Override
    public void startUpdate(ViewGroup container) {}

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // If we already have this item instantiated, there is nothing
        // to do. This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.

        // DONE Remove of the add process of the old stuff
        /* if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { return f; } } */

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        Fragment fragment = getItem(position);
        if (DEBUG)
            Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        if (mSavedState.size() > position) {
            Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {
                try // DONE: Try Catch
                {
                    fragment.setInitialSavedState(fss);
                } catch (Exception ex) {
                    // Schon aktiv (kA was das heißt xD)
                }
            }
        }
        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        fragment.setMenuVisibility(false);
        mFragments.set(position, fragment);
        mCurTransaction.add(container.getId(), fragment);

        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        mCurTransaction.remove(fragment);

        /*if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + " v=" + ((Fragment)
         * object).getView()); while (mSavedState.size() <= position) { mSavedState.add(null); } mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
         * mFragments.set(position, null); mCurTransaction.remove(fragment); */
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

    @Override
    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
        }
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return ((Fragment) object).getView() == view;
    }

    @Override
    public Parcelable saveState() {
        Bundle state = null;
        if (mSavedState.size() > 0) {
            state = new Bundle();
            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
            mSavedState.toArray(fss);
            state.putParcelableArray("states", fss);
        }
        for (int i = 0; i < mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle) state;
            bundle.setClassLoader(loader);
            Parcelable[] fss = bundle.getParcelableArray("states");
            mSavedState.clear();
            mFragments.clear();
            if (fss != null) {
                for (int i = 0; i < fss.length; i++) {
                    mSavedState.add((Fragment.SavedState) fss[i]);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key : keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        while (mFragments.size() <= index) {
                            mFragments.add(null);
                        }
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
        }
    }
}

Here's my normal FragmentAdapter.java

package com.tundem.webLab.Adapter;

import java.util.LinkedList;
import java.util.List;

import android.support.v4.app.FragmentManager;

import com.tundem.webLab.fragments.BaseFragment;
import com.viewpagerindicator.TitleProvider;

public class FragmentAdapter extends CustomFragmentStatePagerAdapter implements TitleProvider {
    public List<BaseFragment> fragments = new LinkedList<BaseFragment>();

    private int actPage;

    public FragmentAdapter(FragmentManager fm) {
        super(fm);
    }

    public void setActPage(int actPage) {
        this.actPage = actPage;
    }

    public void addItem(BaseFragment fragment) {
        // TODO if exists don't open / change to that tab
        fragments.add(fragment);
    }

    public BaseFragment getActFragment() {
        return getItem(getActPage());
    }

    public int getActPage() {
        return actPage;
    }

    @Override
    public BaseFragment getItem(int position) {
        if (position < getCount()) {
            return fragments.get(position);
        } else
            return null;
    }

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

    @Override
    public String getTitle(int position) {
        return fragments.get(position).getTitle();
    }

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

And this is the way i delete a Fragment. (I know it's a bit more than only .remove() ). Be free to improve my solution, you can also add this code somewhere in the adapter so yeah. It's up to the user who tries to implement this. I use this in my TabHelper.java (A class which handles all tab operations like delete, add, ...)

    int act = Cfg.mPager.getCurrentItem();
    Cfg.mPager.removeAllViews();
    Cfg.mAdapter.mFragments.remove(act);
    try {
        Cfg.mAdapter.mSavedState.remove(act);
    } catch (Exception ex) {/* Already removed */}
    try {
        Cfg.mAdapter.fragments.remove(act);
    } catch (Exception ex) {/* Already removed */}

    Cfg.mAdapter.notifyDataSetChanged();
    Cfg.mIndicator.notifyDataSetChanged();

Description of the Cfg. thing. I save the reference to those objects in a cfg, class so i can always use them without the need of a special Factory.java ...

Yeah. i hope i was able to help. Feel free to improve this, but let me know so i can improve my code too.

Thanks.

If i missed any code let me know.


My old answer also works but only if you have different Fragments. FileFragment, WebFragment, ... Not if you use one of those fragmenttypes twice.

I got it pseudo working for now. It's a really dirty solution and i'm still searching for a better one. Please help.

I changed the code, where i delete a tab to this one:

   public static void deleteActTab()
        {   
            //We set this on the indicator, NOT the pager
            int act = Cfg.mPager.getCurrentItem();
            Cfg.mAdapter.removeItem(act);
            List<BaseFragment> frags = new LinkedList<BaseFragment>();
            frags = Cfg.mAdapter.fragments;

            Cfg.mPager = (ViewPager)Cfg.act.findViewById(R.id.pager);
            Cfg.mPager.setAdapter(Cfg.mAdapter);
            Cfg.mIndicator.setViewPager(Cfg.mPager);

            Cfg.mAdapter.fragments = frags;

            if(act > 0)
            {
                Cfg.mPager.setCurrentItem(act-1);
                Cfg.mIndicator.setCurrentItem(act-1);
            }

            Cfg.mIndicator.notifyDataSetChanged();
        }

If someone can improve this code let me know. If someone can tell us the real answer for that problem. please add it here. There are many many people who experience this issue. I added a reputation of 50 for the one who solve it. I can also give a donation for the one who solve it.

Thanks