Im trying to implement switching of fragments inside first tab of view pager.I tried with approach found here link which uses root fragment in first tab and then switches child fragments. But when in second child fragment screen rotation occurs app switches back to first child fragment and besides that it seems like a hack.
I tried searching for other approaches but found dozens of answers that all seems messy.
Explanation from here seems right but i dont fully understand it.Here is the explanation and code from that link:
This approach is to switch the fragment that displays inside the FragmentPagerAdapter by overriding the getItem(...) method as well as getItemPosition(Object object) which is invoked every time you call viewPager.getAdapter().notifyDataSetChanged(). For example:
public static class MyPagerAdapter extends FragmentStatePagerAdapter {
// Return a different fragment for position based on additional state tracked in a member variable
#Override
public Fragment getItem(int position) {
// For a given position, return two different potential fragments based on a condition
}
// Force a refresh of the page when a different fragment is displayed
#Override
public int getItemPosition(Object object) {
// this method will be called for every fragment in the ViewPager
if (object instanceof SomePermanantCachedFragment) {
return POSITION_UNCHANGED; // don't force a reload
} else {
// POSITION_NONE means something like: this fragment is no longer valid
// triggering the ViewPager to re-build the instance of this fragment.
return POSITION_NONE;
}
}
}
Lets say i have ListFragment and DetailFragment that should switch in first tab.
Where will i call viewPager.getAdapter().notifyDataSetChanged() ?
(...based on additional state tracked in a member variable) how do i implement this?
SomePermanantCachedFragment refers to ListFragment in my case?
I also tried searching github for example but with no success...
If your fragments in a PagerAdapter are dynamic, then you should use a Dynamic FragmentPagerAdapter.
public class DynamicFragmentPagerAdapter extends PagerAdapter {
private static final String TAG = "DynamicFragmentPagerAdapter";
private final FragmentManager fragmentManager;
public static abstract class FragmentIdentifier implements Parcelable {
private final String fragmentTag;
private final Bundle args;
public FragmentIdentifier(#NonNull String fragmentTag, #Nullable Bundle args) {
this.fragmentTag = fragmentTag;
this.args = args;
}
protected FragmentIdentifier(Parcel in) {
fragmentTag = in.readString();
args = in.readBundle(getClass().getClassLoader());
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(fragmentTag);
dest.writeBundle(args);
}
protected final Fragment newFragment() {
Fragment fragment = createFragment();
Bundle oldArgs = fragment.getArguments();
Bundle newArgs = new Bundle();
if(oldArgs != null) {
newArgs.putAll(oldArgs);
}
if(args != null) {
newArgs.putAll(args);
}
fragment.setArguments(newArgs);
return fragment;
}
protected abstract Fragment createFragment();
}
private ArrayList<FragmentIdentifier> fragmentIdentifiers = new ArrayList<>();
private FragmentTransaction currentTransaction = null;
private Fragment currentPrimaryItem = null;
public DynamicFragmentPagerAdapter(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
}
private int findIndexIfAdded(FragmentIdentifier fragmentIdentifier) {
for (int i = 0, size = fragmentIdentifiers.size(); i < size; i++) {
FragmentIdentifier identifier = fragmentIdentifiers.get(i);
if (identifier.fragmentTag.equals(fragmentIdentifier.fragmentTag)) {
return i;
}
}
return -1;
}
public void addFragment(FragmentIdentifier fragmentIdentifier) {
if (findIndexIfAdded(fragmentIdentifier) < 0) {
fragmentIdentifiers.add(fragmentIdentifier);
notifyDataSetChanged();
}
}
public void removeFragment(FragmentIdentifier fragmentIdentifier) {
int index = findIndexIfAdded(fragmentIdentifier);
if (index >= 0) {
fragmentIdentifiers.remove(index);
notifyDataSetChanged();
}
}
#Override
public int getCount() {
return fragmentIdentifiers.size();
}
#Override
public void startUpdate(#NonNull ViewGroup container) {
if (container.getId() == View.NO_ID) {
throw new IllegalStateException("ViewPager with adapter " + this
+ " requires a view id");
}
}
#SuppressWarnings("ReferenceEquality")
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int position) {
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
final FragmentIdentifier fragmentIdentifier = fragmentIdentifiers.get(position);
// Do we already have this fragment?
final String name = fragmentIdentifier.fragmentTag;
Fragment fragment = fragmentManager.findFragmentByTag(name);
if (fragment != null) {
currentTransaction.attach(fragment);
} else {
fragment = fragmentIdentifier.newFragment();
currentTransaction.add(container.getId(), fragment, fragmentIdentifier.fragmentTag);
}
if (fragment != currentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
#Override
public void destroyItem(#NonNull ViewGroup container, int position, #NonNull Object object) {
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
currentTransaction.detach((Fragment) object);
}
#SuppressWarnings("ReferenceEquality")
#Override
public void setPrimaryItem(#NonNull ViewGroup container, int position, #NonNull Object object) {
Fragment fragment = (Fragment) object;
if (fragment != currentPrimaryItem) {
if (currentPrimaryItem != null) {
currentPrimaryItem.setMenuVisibility(false);
currentPrimaryItem.setUserVisibleHint(false);
}
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
currentPrimaryItem = fragment;
}
}
#Override
public void finishUpdate(#NonNull ViewGroup container) {
if (currentTransaction != null) {
currentTransaction.commitNowAllowingStateLoss();
currentTransaction = null;
}
}
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object object) {
return ((Fragment) object).getView() == view;
}
#Override
public Parcelable saveState() {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("fragmentIdentifiers", fragmentIdentifiers);
return bundle;
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
Bundle bundle = ((Bundle)state);
bundle.setClassLoader(loader);
fragmentIdentifiers = bundle.getParcelableArrayList("fragmentIdentifiers");
}
}
Once you have that, you can use removeFragment and addFragment to change the fragments inside your ViewPager.
You could technically add a setFragment(int index) method to replace it at index 0 specifically.
If the fragments don't seem to update, the trick to refreshing a ViewPager is always setAdapter(null); setAdapter(adapter);.
Working on app that needs to dynamically add and remove list fragments from viewpager, when tapped on an item in the list new fragment adds to viewpager and when swipe back fragment needs to be removed, this works good but when i rotate the screen i get double instances of fragments in the pager, rotate it again and it quadruples the instances.
At the same time i need to persist state of the fragments (list position, number of items loaded, etc) this means overriding onsaveinstancestate in fragments and saving relevant data in the bundle to restore on recreation.
I managed to solve the issue of double instances by clearing the adapter and calling notifydatasetchanged but then i lose saving states in fragment since onsaveinstance is not called for obvious reasons and if i don't clear the adapter it just doubles the instances. I have seen the same behavior in Dropbox app when entering and leaving folders.
This is custom pager adapter implementation i am using
/**
* Implementation of {#link PagerAdapter} that
* uses a {#link Fragment} to manage each page. This class also handles
* saving and restoring of fragment's state.
*
* <p>This version of the pager is more useful when there are a large number
* of pages, working more like a list view. When pages are not visible to
* the user, their entire fragment may be destroyed, only keeping the saved
* state of that fragment. This allows the pager to hold on to much less
* memory associated with each visited page as compared to
* {#link FragmentPagerAdapter} at the cost of potentially more overhead when
* switching between pages.
*
* <p>When using FragmentPagerAdapter the host ViewPager must have a
* valid ID set.</p>
*
* <p>Subclasses only need to implement {#link #getItem(int)}
* and {#link #getCount()} to have a working adapter. They also should
* override {#link #getItemId(int)} if the position of the items can change.
*/
public abstract class UpdatableFragmentPagerAdapter extends PagerAdapter {
private final FragmentManager fragmentManager;
private final LongSparseArray<Fragment> fragmentList = new LongSparseArray<>();
private final LongSparseArray<Fragment.SavedState> savedStatesList = new LongSparseArray<>();
#Nullable private FragmentTransaction currentTransaction = null;
#Nullable private Fragment currentPrimaryItem = null;
public UpdatableFragmentPagerAdapter(#NonNull FragmentManager fm) {
this.fragmentManager = fm;
}
/**
* Return the Fragment associated with a specified position.
*/
public abstract Fragment getItem(int position);
#Override public void startUpdate(#NonNull ViewGroup container) {
if (container.getId() == View.NO_ID) {
throw new IllegalStateException("ViewPager with adapter " + this + " requires a view id");
}
}
#Override #NonNull public Object instantiateItem(ViewGroup container, int position) {
long tag = getItemId(position);
Fragment fragment = fragmentList.get(tag);
// 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.
if (fragment != null) {
return fragment;
}
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
fragment = getItem(position);
// restore state
final Fragment.SavedState savedState = savedStatesList.get(tag);
if (savedState != null) {
fragment.setInitialSavedState(savedState);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
fragmentList.put(tag, fragment);
currentTransaction.add(container.getId(), fragment, "f" + tag);
return fragment;
}
#Override public void destroyItem(ViewGroup container, int position, #NonNull Object object) {
Fragment fragment = (Fragment) object;
int currentPosition = getItemPosition(fragment);
int index = fragmentList.indexOfValue(fragment);
long fragmentKey = -1;
if (index != -1) {
fragmentKey = fragmentList.keyAt(index);
fragmentList.removeAt(index);
}
//item hasn't been removed
if (fragment.isAdded() && currentPosition != POSITION_NONE) {
savedStatesList.put(fragmentKey, fragmentManager.saveFragmentInstanceState(fragment));
} else {
savedStatesList.remove(fragmentKey);
}
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
currentTransaction.remove(fragment);
}
#Override public void setPrimaryItem(ViewGroup container, int position, #Nullable Object object) {
Fragment fragment = (Fragment) object;
if (fragment != currentPrimaryItem) {
if (currentPrimaryItem != null) {
currentPrimaryItem.setMenuVisibility(false);
currentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
currentPrimaryItem = fragment;
}
}
#Override public void finishUpdate(ViewGroup container) {
if (currentTransaction != null) {
currentTransaction.commitNowAllowingStateLoss();
currentTransaction = null;
}
}
#Override public boolean isViewFromObject(#NonNull View view, #NonNull Object object) {
return ((Fragment) object).getView() == view;
}
#Override public Parcelable saveState() {
Bundle state = null;
if (savedStatesList.size() > 0) {
// save Fragment states
state = new Bundle();
long[] stateIds = new long[savedStatesList.size()];
for (int i = 0; i < savedStatesList.size(); i++) {
Fragment.SavedState entry = savedStatesList.valueAt(i);
stateIds[i] = savedStatesList.keyAt(i);
state.putParcelable(Long.toString(stateIds[i]), entry);
}
state.putLongArray("states", stateIds);
}
for (int i = 0; i < fragmentList.size(); i++) {
Fragment f = fragmentList.valueAt(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + fragmentList.keyAt(i);
fragmentManager.putFragment(state, key, f);
}
}
return state;
}
#Override public void restoreState(#Nullable Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle) state;
bundle.setClassLoader(loader);
long[] fss = bundle.getLongArray("states");
savedStatesList.clear();
fragmentList.clear();
if (fss != null) {
for (long fs : fss) {
savedStatesList.put(fs, bundle.getParcelable(Long.toString(fs)));
}
}
Iterable<String> keys = bundle.keySet();
for (String key : keys) {
if (key.startsWith("f")) {
Fragment f = fragmentManager.getFragment(bundle, key);
if (f != null) {
f.setMenuVisibility(false);
fragmentList.put(Long.parseLong(key.substring(1)), f);
} else {
Timber.w("Bad fragment at key %s", key);
}
}
}
}
}
/**
* Return a unique identifier for the item at the given position.
* <p>
* <p>The default implementation returns the given position.
* Subclasses should override this method if the positions of items can change.</p>
*
* #param position Position within this adapter
* #return Unique identifier for the item at position
*/
public long getItemId(int position) {
return position;
}
}
This is the implementation of adapter
class FolderPagerAdapter extends UpdatableFragmentPagerAdapter {
private final FragmentManager fragmentManager;
// Sparse array to keep track of registered fragments in memory
private List<Fragment> addedFragments;
FolderPagerAdapter(FragmentManager fm) {
super(fm);
this.fragmentManager = fm;
}
void init() {
if (addedFragments == null) {
addedFragments = new ArrayList<>();
}
addedFragments.clear();
addedFragments.add(CollectionsListFragment.newInstance());
notifyDataSetChanged();
}
#Override public Fragment getItem(int position) {
return addedFragments.get(position);
}
#Override public long getItemId(int position) {
return addedFragments.get(position).hashCode();
}
#Override public int getCount() {
return addedFragments.size();
}
//this is called when notifyDataSetChanged() is called
#Override public int getItemPosition(Object object) {
//// refresh all fragments when data set changed
Fragment fragment = (Fragment) object;
if (fragment instanceof CollectionFragment) {
return POSITION_UNCHANGED;
} else {
int hashCode = fragment.hashCode();
for (int i = 0; i < addedFragments.size(); i++) {
if (addedFragments.get(i).hashCode() == hashCode) {
return i;
}
}
}
return PagerAdapter.POSITION_NONE;
}
void removeLastPage() {
addedFragments.remove(addedFragments.size() - 1);
notifyDataSetChanged();
}
void addCollectionFragment(CollectionFragment collectionFragment) {
addedFragments.add(collectionFragment);
notifyDataSetChanged();
}
void addFolderFragment(FolderFragment folderFragment) {
addedFragments.add(folderFragment);
notifyDataSetChanged();
}
void restoreFragments(List<PagerFolderCollectionModel> pagesList) {
if (!pagesList.isEmpty()) {
for (int i = 0; i < pagesList.size(); i++) {
if (i == 0) {
addedFragments.add(CollectionFragment.newInstance(pagesList.get(0).getItemId()));
} else {
addedFragments.add(FolderFragment.newInstance(pagesList.get(i).getItemName()));
}
}
notifyDataSetChanged();
}
}
void removeAll() {
addedFragments.clear();
notifyDataSetChanged();
}
}
and a holder pojo which i am using to save in onsaveinstancestate in activity and restore on rotation
public class PagerFolderCollectionModel implements Parcelable {
public static final Parcelable.Creator<PagerFolderCollectionModel> CREATOR =
new Parcelable.Creator<PagerFolderCollectionModel>() {
#Override public PagerFolderCollectionModel createFromParcel(Parcel source) {
return new PagerFolderCollectionModel(source);
}
#Override public PagerFolderCollectionModel[] newArray(int size) {
return new PagerFolderCollectionModel[size];
}
};
private String itemId;
private String itemName;
public PagerFolderCollectionModel(String itemId, String itemName) {
this.itemId = itemId;
this.itemName = itemName;
}
protected PagerFolderCollectionModel(Parcel in) {
this.itemId = in.readString();
this.itemName = in.readString();
}
public String getItemId() {
return itemId;
}
public String getItemName() {
return itemName;
}
#Override public int describeContents() {
return 0;
}
#Override public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.itemId);
dest.writeString(this.itemName);
}
}
onsaveinstance method in activity
#Override protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(STATE_SELECTED_OPTION, selectedDrawerOption);
outState.putBoolean(STATE_SHOW_GRID_OPTION, isShowGridOption);
outState.putParcelableArrayList(STATE_SHOWN_FRAGMENTS,
(ArrayList<PagerFolderCollectionModel>) adapteritemslist);
Timber.e("save");
}
The requirement is that the first item in the adapter is always collection fragment and folder fragments are added and removed on demand (tap or swipe back)
Is there a solution for this (implementing pager adapter differently, using custom views in adapter...)? Does anybody knows how this is done in Dropbox app?
Can you please try to use the
setRetainInstance(boolean retain)
in the onCreateView method of your fragment. Set it to true.
It Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change).
Have a look at this resources:
How does viewPager retain fragment states on orientation change?
Fragment in ViewPager on Fragment doesn't reload on orientation change
https://stackoverflow.com/a/27316052/2930101
Try to search for the fragments in the fragment manager first when you return them in the ViewPager.
If you manage to solve your issue I would be interested in your solution!
I have managed to fix this by editing this code
#Override public long getItemId(int position) {
return addedFragments.get(position).hashCode();
}
The issue was that the hashCode is generated and different on each rotation and since the requirement is not to change the position of pages i have just removed this method. It works now as expected, you can add and remove fragments with restoring state on orientation change.
I want to create app with dynamic add/remove tabs option and I've got problem with one thing.
When I'm adding tabs first time everything is ok but when I remove one tab and I want to add new tab again then there is no onCreateView called on that new fragment.
Example:
I add new 3 tabs:
Add tab 1:
ViewPagerAdapter vpa = (ViewPagerAdapter) mViewPager.getAdapter();
vpa.addTab("TAB 1");
vpa.notifyDataSetChanged();
onCreateView is called.
some logs:
[add tab] mViewPager.getChildCount() = 1
[add tab] vpa.getCount() = 1
Add tab 2:
vpa.addTab("TAB 2");
vpa.notifyDataSetChanged();
onCreateView is called.
some logs:
[add tab] mViewPager.getChildCount() = 2
[add tab] vpa.getCount() = 2
Add tab 3:
vpa.addTab("TAB 3");
vpa.notifyDataSetChanged();
onCreateView is called.
some logs:
[add tab] mViewPager.getChildCount() = 3
[add tab] vpa.getCount() = 3
now I want to delete Tab 2:
vpa.removeTab("TAB 2");
vpa.notifyDataSetChanged();
some logs:
[add tab] mViewPager.getChildCount() = 2
[add tab] vpa.getCount() = 2
and I want to add Tab 2 again:
vpa.addTab("TAB 2");
vpa.notifyDataSetChanged();
and there is no onCreateView called :(
some logs:
[add tab] mViewPager.getChildCount() = 2
[add tab] vpa.getCount() = 3
Can anyone know how to fix that? Why onCreateView is not always called?
App code:
private void initViewpagerAndTabs(){
mViewPager = (ViewPager) findViewById(R.id.viewpager_slave);
mViewPager.setOffscreenPageLimit(10);
mAdapterViewPager = new ViewPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mAdapterViewPager);
mTabLayout = (TabLayout) findViewById(R.id.tabLayout_slave);
mTabLayout.setupWithViewPager(mViewPager);
}
/** Tabs content fragment */
public static class DummyFragment extends Fragment implements RecyclerAdapter.ViewHolder.ClickListener {
private RecyclerView mRecyclerView;
private RecyclerAdapter mAdapterRecycler;
private RecyclerView.LayoutManager mLayoutManager;
private Fab fab;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.content_slave, container, false);
//setRetainInstance(true);
initRecyclerView(view);
fab = (Fab) getActivity().findViewById(R.id.fabRemoveItems);
return view;
}
private void initRecyclerView(View view){
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view_slave);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(getContext());
mRecyclerView.setLayoutManager(mLayoutManager);
RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator();
itemAnimator.setAddDuration(500);
itemAnimator.setRemoveDuration(500);
mAdapterRecycler = new RecyclerAdapter(this);
mRecyclerView.setAdapter(mAdapterRecycler);
}
}
private class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final List<DummyFragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
public DummyFragment getItem(int num) throws IndexOutOfBoundsException{
return mFragmentList.get(num);
}
public void addTab(String title) throws Exception {
if(isTabExist(title)){
throw new Exception("Tabs already exist!");
}
//Log.d(TAG, "[addTab] mFragmentTitleList before add: " + mFragmentTitleList);
//Log.d(TAG, "[addTab] mFragmentList before add: " + mFragmentList);
mFragmentList.add(new DummyFragment());
mFragmentTitleList.add(title);
//Log.d(TAG, "[addTab] mFragmentTitleList after add: " + mFragmentTitleList);
//Log.d(TAG, "[addTab] mFragmentList after add: " + mFragmentList);
}
public void removeTab(int position){
//Log.d(TAG, "[removeTab] mFragmentTitleList before removed: " + mFragmentTitleList);
//Log.d(TAG, "[removeTab] mFragmentList before removed: " + mFragmentList);
mFragmentTitleList.remove(position);
mFragmentList.remove(position);
//Log.d(TAG, "[removeTab] mFragmentTitleList after removed: " + mFragmentTitleList);
//Log.d(TAG, "[removeTab] mFragmentList after removed: " + mFragmentList);
}
private boolean isTabExist(String name){
for(int i = 0; i < mFragmentTitleList.size(); i++) {
if (mFragmentTitleList.get(i).equals(name)) {
return true;
}
}
return false;
}
public int getTabPosition(String tabName) throws IndexOutOfBoundsException{
for(int i = 0; i < mFragmentTitleList.size(); i++) {
if (mFragmentTitleList.get(i).equals(tabName)) {
return i;
}
}
throw new IndexOutOfBoundsException();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
getSupportFragmentManager().beginTransaction().remove((Fragment) object)
.commit();
}
#Override
public int getItemPosition(Object object) {
if (mFragmentList.contains(object)) return mFragmentList.indexOf(object);
else return POSITION_NONE;
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
Ok, I resolve that problem.
Delete:
mViewPager.setOffscreenPageLimit(10);
Override methods to save fragments state:
public void onViewStateRestored(#Nullable Bundle savedInstanceState)
public void onSaveInstanceState(Bundle outState)
And I've created my own FragmentStatePagerAdapter class. I needed to don't save fragment state on tab which was deleted.
Code:
public class FragStatePagerAdaper extends PagerAdapter {
private static final String TAG = "FragStatePagerAdapter";
private static final boolean DEBUG = true;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
/** Cached fragments */
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
private Fragment mCurrentPrimaryItem = null;
private List<DummyFragment> mFragmentList = new ArrayList<>();
private List<String> mFragmentTitleList = new ArrayList<>();
public FragStatePagerAdaper(FragmentManager fm) {
mFragmentManager = fm;
}
/**
* Return the Fragment associated with a specified position.
*/
public DummyFragment getItem(int num) throws IndexOutOfBoundsException{
return mFragmentList.get(num);
}
#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.
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) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(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();
}
if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment)object).getView());
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
mCurTransaction.remove(fragment);
}
public void addTab(String title) throws Exception {
if(isTabExist(title)){
throw new Exception("Tabs already exist!");
}
mFragmentList.add(new DummyFragment());
mFragmentTitleList.add(title);
}
//look here!
public void removeTab(int position){
Log.d(TAG, "removeTab: position = " + position);
mFragmentTitleList.remove(position);
mFragmentList.remove(position);
mSavedState.remove(position); // <---- here!
}
private boolean isTabExist(String name){
for(int i = 0; i < mFragmentTitleList.size(); i++) {
if (mFragmentTitleList.get(i).equals(name)) {
return true;
}
}
return false;
}
public int getTabPosition(String tabName) throws IndexOutOfBoundsException{
for(int i = 0; i < mFragmentTitleList.size(); i++) {
if (mFragmentTitleList.get(i).equals(tabName)) {
return i;
}
}
throw new IndexOutOfBoundsException();
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
#Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.executePendingTransactions();
}
ArrayList<Fragment> update = new ArrayList<Fragment>();
for (int i=0, n=mFragments.size(); i < n; i++) {
Fragment f = mFragments.get(i);
if (f == null) continue;
int pos = getItemPosition(f);
while (update.size() <= pos) {
update.add(null);
}
update.set(pos, f);
}
mFragments = update;
}
#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 && f.isAdded()) {
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);
}
}
}
}
}
#Override
public int getItemPosition(Object object) {
if (mFragmentList.contains(object)) return mFragmentList.indexOf(object);
else return POSITION_NONE;
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
Greetings!
I'm trying to dynamically add and remove Fragments from a ViewPager, adding works without any problems, but removing doesn't work as expected.
Everytime I want to remove the current item, the last one gets removed.
I also tried to use an FragmentStatePagerAdapter or return POSITION_NONE in the adapter's getItemPosition method.
What am I doing wrong?
Here's a basic example:
MainActivity.java
public class MainActivity extends FragmentActivity implements TextProvider {
private Button mAdd;
private Button mRemove;
private ViewPager mPager;
private MyPagerAdapter mAdapter;
private ArrayList<String> mEntries = new ArrayList<String>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mEntries.add("pos 1");
mEntries.add("pos 2");
mEntries.add("pos 3");
mEntries.add("pos 4");
mEntries.add("pos 5");
mAdd = (Button) findViewById(R.id.add);
mRemove = (Button) findViewById(R.id.remove);
mPager = (ViewPager) findViewById(R.id.pager);
mAdd.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
addNewItem();
}
});
mRemove.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
removeCurrentItem();
}
});
mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);
mPager.setAdapter(mAdapter);
}
private void addNewItem() {
mEntries.add("new item");
mAdapter.notifyDataSetChanged();
}
private void removeCurrentItem() {
int position = mPager.getCurrentItem();
mEntries.remove(position);
mAdapter.notifyDataSetChanged();
}
#Override
public String getTextForPosition(int position) {
return mEntries.get(position);
}
#Override
public int getCount() {
return mEntries.size();
}
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
#Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}
#Override
public int getCount() {
return mProvider.getCount();
}
}
}
TextProvider.java
public interface TextProvider {
public String getTextForPosition(int position);
public int getCount();
}
MyFragment.java
public class MyFragment extends Fragment {
private String mText;
public static MyFragment newInstance(String text) {
MyFragment f = new MyFragment(text);
return f;
}
public MyFragment() {
}
public MyFragment(String text) {
this.mText = text;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment, container, false);
((TextView) root.findViewById(R.id.position)).setText(mText);
return root;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add new item" />
<Button
android:id="#+id/remove"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="remove current item" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>
fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/position"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="35sp" />
</LinearLayout>
The ViewPager doesn't remove your fragments with the code above because it loads several views (or fragments in your case) into memory. In addition to the visible view, it also loads the view to either side of the visible one. This provides the smooth scrolling from view to view that makes the ViewPager so cool.
To achieve the effect you want, you need to do a couple of things.
Change the FragmentPagerAdapter to a FragmentStatePagerAdapter. The reason for this is that the FragmentPagerAdapter will keep all the views that it loads into memory forever. Where the FragmentStatePagerAdapter disposes of views that fall outside the current and traversable views.
Override the adapter method getItemPosition (shown below). When we call mAdapter.notifyDataSetChanged(); the ViewPager interrogates the adapter to determine what has changed in terms of positioning. We use this method to say that everything has changed so reprocess all your view positioning.
And here's the code...
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
#Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
}
The solution by Louth was not enough to get things working for me, as the existing fragments were not getting destroyed. Motivated by this answer, I found that the solution is to override the getItemId(int position) method of FragmentPagerAdapter to give a new unique ID whenever there has been a change in the expected position of a Fragment.
Source Code:
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
private long baseId = 0;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
#Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}
#Override
public int getCount() {
return mProvider.getCount();
}
//this is called when notifyDataSetChanged() is called
#Override
public int getItemPosition(Object object) {
// refresh all fragments when data set changed
return PagerAdapter.POSITION_NONE;
}
#Override
public long getItemId(int position) {
// give an ID different from position when position has been changed
return baseId + position;
}
/**
* Notify that the position of a fragment has been changed.
* Create a new ID for each position to force recreation of the fragment
* #param n number of items which have been changed
*/
public void notifyChangeInPosition(int n) {
// shift the ID returned by getItemId outside the range of all previous fragments
baseId += getCount() + n;
}
}
Now, for example if you delete a single tab or make some change to the order, you should call notifyChangeInPosition(1) before calling notifyDataSetChanged(), which will ensure that all the Fragments will be recreated.
Why this solution works
Overriding getItemPosition():
When notifyDataSetChanged() is called, the adapter calls the notifyChanged() method of the ViewPager which it is attached to. The ViewPager then checks the value returned by the adapter's getItemPosition() for each item, removing those items which return POSITION_NONE (see the source code) and then repopulating.
Overriding getItemId():
This is necessary to prevent the adapter from reloading the old fragment when the ViewPager is repopulating. You can easily understand why this works by looking at the source code for instantiateItem() in FragmentPagerAdapter.
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
As you can see, the getItem() method is only called if the fragment manager finds no existing fragments with the same Id. To me it seems like a bug that the old fragments are still attached even after notifyDataSetChanged() is called, but the documentation for ViewPager does clearly state that:
Note this class is currently under early design and development. The API will likely change in later updates of the compatibility library, requiring changes to the source code of apps when they are compiled against the newer version.
So hopefully the workaround given here will not be necessary in a future version of the support library.
my working solution to remove fragment page from view pager
public class MyFragmentAdapter extends FragmentStatePagerAdapter {
private ArrayList<ItemFragment> pages;
public MyFragmentAdapter(FragmentManager fragmentManager, ArrayList<ItemFragment> pages) {
super(fragmentManager);
this.pages = pages;
}
#Override
public Fragment getItem(int index) {
return pages.get(index);
}
#Override
public int getCount() {
return pages.size();
}
#Override
public int getItemPosition(Object object) {
int index = pages.indexOf (object);
if (index == -1)
return POSITION_NONE;
else
return index;
}
}
And when i need to remove some page by index i do this
pages.remove(position); // ArrayList<ItemFragment>
adapter.notifyDataSetChanged(); // MyFragmentAdapter
Here it is my adapter initialization
MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
The fragment must be already removed but the issue was viewpager save state
Try
myViewPager.setSaveFromParentEnabled(false);
Nothing worked but this solved the issue !
Cheers !
I had the idea of simply copy the source code from android.support.v4.app.FragmentPagerAdpater into a custom class named
CustumFragmentPagerAdapter. This gave me the chance to modify the instantiateItem(...) so that every time it is called, it removes / destroys the currently attached fragment before it adds the new fragment received from getItem() method.
Simply modify the instantiateItem(...) in the following way:
#Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
// remove / destroy current fragment
if (fragment != null) {
mCurTransaction.remove(fragment);
}
// get new fragment and add it
fragment = getItem(position);
mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
You can combine both for better :
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
#Override
public int getItemPosition(Object object){
if(Any_Reason_You_WantTo_Update_Positions) //this includes deleting or adding pages
return PagerAdapter.POSITION_NONE;
}
else
return PagerAdapter.POSITION_UNCHANGED; //this ensures high performance in other operations such as editing list items.
}
I had some problems with FragmentStatePagerAdapter.
After removing an item:
there was another item used for a position (an item which did not belong to the position but to a position next to it)
or some fragment was not loaded (there was only blank background visible on that page)
After lots of experiments, I came up with the following solution.
public class SomeAdapter extends FragmentStatePagerAdapter {
private List<Item> items = new ArrayList<Item>();
private boolean removing;
#Override
public Fragment getItem(int position) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
// use items.get(position) to configure fragment
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
return items.size();
}
#Override
public int getItemPosition(Object object) {
if (removing) {
return PagerAdapter.POSITION_NONE;
}
Item item = getItemOfFragment(object);
int index = items.indexOf(item);
if (index == -1) {
return POSITION_NONE;
} else {
return index;
}
}
public void addItem(Item item) {
items.add(item);
notifyDataSetChanged();
}
public void removeItem(int position) {
items.remove(position);
removing = true;
notifyDataSetChanged();
removing = false;
}
}
This solution only uses a hack in case of removing an item. Otherwise (e.g. when adding an item) it retains the cleanliness and performance of an original code.
Of course, from the outside of the adapter, you call only addItem/removeItem, no need to call notifyDataSetChanged().
For future readers!
Now you can use ViewPager2 for dynamically adding, removing fragment from the viewpager.
Quoting form API reference
ViewPager2 replaces ViewPager, addressing most of its predecessor’s
pain-points, including right-to-left layout support, vertical
orientation, modifiable Fragment collections, etc.
Take look at MutableCollectionFragmentActivity.kt in googlesample/android-viewpager2 for an example of adding, removing fragments dynamically from the viewpager.
For your information:
Articles:
Exploring the ViewPager2
Look deep into ViewPager2
API reference
Release notes
Samples Repo: https://github.com/googlesamples/android-viewpager2
Louth's answer works fine. But I don't think always return POSITION_NONE is a good idea. Because POSITION_NONE means that fragment should be destroyed and a new fragment will be created.
You can check that in dataSetChanged function in the source code of ViewPager.
if (newPos == PagerAdapter.POSITION_NONE) {
mItems.remove(i);
i--;
... not related code
mAdapter.destroyItem(this, ii.position, ii.object);
So I think you'd better use an arraylist of weakReference to save all the fragments you have created. And when you add or remove some page, you can get the right position from your own arraylist.
public int getItemPosition(Object object) {
for (int i = 0; i < mFragmentsReferences.size(); i ++) {
WeakReference<Fragment> reference = mFragmentsReferences.get(i);
if (reference != null && reference.get() != null) {
Fragment fragment = reference.get();
if (fragment == object) {
return i;
}
}
}
return POSITION_NONE;
}
According to the comments, getItemPosition is Called when the host view is attempting to determine if an item's position has changed. And the return value means its new position.
But this is not enought. We still have an important step to take.
In the source code of FragmentStatePagerAdapter, there is an array named "mFragments" caches the fragments which are not destroyed. And in instantiateItem function.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
It returned the cached fragment directly when it find that cached fragment is not null. So there is a problem. From example, let's delete one page at position 2, Firstly, We remove that fragment from our own reference arraylist. so in getItemPosition it will return POSITION_NONE for that fragment, and then that fragment will be destroyed and removed from "mFragments".
mFragments.set(position, null);
Now the fragment at position 3 will be at position 2. And instantiatedItem with param position 3 will be called. At this time, the third item in "mFramgents" is not null, so it will return directly. But actually what it returned is the fragment at position 2. So when we turn into page 3, we will find an empty page there.
To work around this problem. My advise is that you can copy the source code of FragmentStatePagerAdapter into your own project, and when you do add or remove operations, you should add and remove elements in the "mFragments" arraylist.
Things will be simpler if you just use PagerAdapter instead of FragmentStatePagerAdapter. Good Luck.
add or remove fragment in viewpager dynamically.
Call setupViewPager(viewPager) on activity start.
To load different fragment call setupViewPagerCustom(viewPager).
e.g. on button click call: setupViewPagerCustom(viewPager);
private void setupViewPager(ViewPager viewPager)
{
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new fragmnet1(), "HOME");
adapter.addFrag(new fragmnet2(), "SERVICES");
viewPager.setAdapter(adapter);
}
private void setupViewPagerCustom(ViewPager viewPager)
{
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new fragmnet3(), "Contact us");
adapter.addFrag(new fragmnet4(), "ABOUT US");
viewPager.setAdapter(adapter);
}
//Viewpageradapter, handles the views
static class ViewPagerAdapter extends FragmentStatePagerAdapter
{
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager){
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
public void addFrag(Fragment fragment, String title){
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position){
return mFragmentTitleList.get(position);
}
}
Try this solution. I have used databinding for binding view. You can use common "findViewById()" function.
public class ActCPExpense extends BaseActivity implements View.OnClickListener, {
private static final String TAG = ActCPExpense.class.getSimpleName();
private Context mContext;
private ActCpLossBinding mBinding;
private ViewPagerAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
setContentView(R.layout.act_cp_loss);
mBinding = DataBindingUtil.setContentView(this, R.layout.act_cp_loss);
mContext = ActCPExpense.this;
initViewsAct();
} catch (Exception e) {
LogUtils.LOGE(TAG, e);
}
}
private void initViewsAct() {
adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(FragmentCPPayee.newInstance(), "Title");
mBinding.viewpager.setAdapter(adapter);
mBinding.tab.setViewPager(mBinding.viewpager);
}
#Override
public boolean onOptionsItemSelected(MenuItem itemActUtility) {
int i = itemActUtility.getItemId();
if (i == android.R.id.home) {
onBackPressed();
}
return super.onOptionsItemSelected(itemActUtility);
}
#Override
public void onClick(View view) {
super.onClick(view);
int id = view.getId();
if (id == R.id.btnAdd) {
addFragment();
} else if (id == R.id.btnDelete) {
removeFragment();
}
}
private void addFragment(){
adapter.addFragment(FragmentCPPayee.newInstance("Title");
adapter.notifyDataSetChanged();
mBinding.tab.setViewPager(mBinding.viewpager);
}
private void removeFragment(){
adapter.removeItem(mBinding.viewpager.getCurrentItem());
mBinding.tab.setViewPager(mBinding.viewpager);
}
class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public int getItemPosition(#NonNull Object object) {
return PagerAdapter.POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public void removeItem(int pos) {
destroyItem(null, pos, mFragmentList.get(pos));
mFragmentList.remove(pos);
mFragmentTitleList.remove(pos);
adapter.notifyDataSetChanged();
mBinding.viewpager.setCurrentItem(pos - 1, false);
}
#Override
public CharSequence getPageTitle(int position) {
return "Title " + String.valueOf(position + 1);
}
}
}
I added a function "clearFragments" and I used that function to clear adapter before setting the new fragments. This calls the proper remove actions of Fragments. My pagerAdapter class:
private class ChartPagerAdapter extends FragmentPagerAdapter{
private ArrayList<Fragment> fragmentList;
ChartPagerAdapter(FragmentManager fm){
super(fm);
fragmentList = new ArrayList<>();
}
void setFragments(ArrayList<? extends Fragment> fragments){
fragmentList.addAll(fragments);
}
void clearFragments(){
for(Fragment fragment:fragmentList)
getChildFragmentManager().beginTransaction().remove(fragment).commit();
fragmentList.clear();
}
#Override
public Fragment getItem(int i) {
return fragmentList.get(i);
}
#Override
public int getCount() {
return fragmentList.size();
}
}
i solved this problem by these steps
1- use FragmentPagerAdapter
2- in each fragment create a random id
fragment.id = new Random().nextInt();
3- override getItemPosition in adapter
#Override
public int getItemPosition(#NonNull Object object) {
return PagerAdapter.POSITION_NONE;
}
4-override getItemId in adapter
#Override
public long getItemId(int position) {
return mDatasetFragments.get(position).id;
}
5- now delete code is
adapter.mDatasetFragments.remove(< item to delete position >);
adapter.notifyDataSetChanged();
this worked for me i hope help
My final version code, fixed ALL bugs. It took me 3 days
Updated 2020/07/18:
I had changed a lot of the source code and fix so many bugs, but I don't promise it still work today.
https://github.com/lin1987www/FragmentBuilder/blob/master/commonLibrary/src/main/java/android/support/v4/app/FragmentStatePagerAdapterFix.java
public class FragmentStatePagerAdapterFix extends PagerAdapter {
private static final String TAG = FragmentStatePagerAdapterFix.class.getSimpleName();
private static final boolean DEBUG = false;
private WeakReference<FragmentActivity> wrFragmentActivity;
private WeakReference<Fragment> wrParentFragment;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
protected ArrayList<Fragment> mFragments = new ArrayList<>();
protected ArrayList<FragmentState> mFragmentStates = new ArrayList<>();
protected ArrayList<String> mFragmentTags = new ArrayList<>();
protected ArrayList<String> mFragmentClassNames = new ArrayList<>();
protected ArrayList<Bundle> mFragmentArgs = new ArrayList<>();
private Fragment mCurrentPrimaryItem = null;
private boolean[] mTempPositionChange;
#Override
public int getCount() {
return mFragmentClassNames.size();
}
public FragmentActivity getFragmentActivity() {
return wrFragmentActivity.get();
}
public Fragment getParentFragment() {
return wrParentFragment.get();
}
public FragmentStatePagerAdapterFix(FragmentActivity activity) {
mFragmentManager = activity.getSupportFragmentManager();
wrFragmentActivity = new WeakReference<>(activity);
wrParentFragment = new WeakReference<>(null);
}
public FragmentStatePagerAdapterFix(Fragment fragment) {
mFragmentManager = fragment.getChildFragmentManager();
wrFragmentActivity = new WeakReference<>(fragment.getActivity());
wrParentFragment = new WeakReference<>(fragment);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass) {
add(fragClass, null, null);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args) {
add(fragClass, args, null);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, String tag) {
add(fragClass, null, tag);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag) {
add(fragClass, args, tag, getCount());
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag, int position) {
mFragments.add(position, null);
mFragmentStates.add(position, null);
mFragmentTags.add(position, tag);
mFragmentClassNames.add(position, fragClass.getName());
mFragmentArgs.add(position, args);
mTempPositionChange = new boolean[getCount()];
}
public void remove(int position) {
if (position < getCount()) {
mTempPositionChange = new boolean[getCount()];
for (int i = position; i < mTempPositionChange.length; i++) {
mTempPositionChange[i] = true;
}
mFragments.remove(position);
mFragmentStates.remove(position);
mFragmentTags.remove(position);
mFragmentClassNames.remove(position);
mFragmentArgs.remove(position);
}
}
public void clear(){
mFragments.clear();
mFragmentStates.clear();
mFragmentTags.clear();
mFragmentClassNames.clear();
mFragmentArgs.clear();
}
#Override
public void startUpdate(ViewGroup container) {
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment;
// 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.
fragment = mFragments.get(position);
if (fragment != null) {
return fragment;
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
FragmentState fs = mFragmentStates.get(position);
if (fs != null) {
fragment = fs.instantiate(getFragmentActivity(), getParentFragment());
// Fix bug
// http://stackoverflow.com/questions/11381470/classnotfoundexception-when-unmarshalling-android-support-v4-view-viewpagersav
if (fragment.mSavedFragmentState != null) {
fragment.mSavedFragmentState.setClassLoader(fragment.getClass().getClassLoader());
}
}
if (fragment == null) {
fragment = Fragment.instantiate(getFragmentActivity(), mFragmentClassNames.get(position), mFragmentArgs.get(position));
}
if (DEBUG) {
Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mFragmentStates.set(position, null);
mCurTransaction.add(container.getId(), fragment, mFragmentTags.get(position));
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) {
Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment) object).getView());
}
if (position < getCount()) {
FragmentState fragmentState = new FragmentState(fragment);
Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment);
if (savedState != null) {
fragmentState.mSavedFragmentState = savedState.mState;
}
mFragmentStates.set(position, fragmentState);
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);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
#Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.executePendingTransactions();
// Fix: Fragment is added by transaction. BUT didn't add to FragmentManager's mActive.
for (Fragment fragment : mFragments) {
if (fragment != null) {
fixActiveFragment(mFragmentManager, fragment);
}
}
}
}
#Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment) object).getView() == view;
}
#Override
public Parcelable saveState() {
Bundle state = null;
// 目前顯示的 Fragments
for (int i = 0; i < mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
if (mFragmentStates.size() > 0) {
if (state == null) {
state = new Bundle();
}
FragmentState[] fs = new FragmentState[mFragmentStates.size()];
mFragmentStates.toArray(fs);
state.putParcelableArray("states_fragment", fs);
}
return state;
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle) state;
bundle.setClassLoader(loader);
Parcelable[] fs = bundle.getParcelableArray("states_fragment");
mFragments.clear();
mFragmentStates.clear();
mFragmentTags.clear();
mFragmentClassNames.clear();
mFragmentArgs.clear();
if (fs != null) {
for (int i = 0; i < fs.length; i++) {
FragmentState fragmentState = (FragmentState) fs[i];
mFragmentStates.add(fragmentState);
if (fragmentState != null) {
mFragmentArgs.add(fragmentState.mArguments);
mFragmentTags.add(fragmentState.mTag);
mFragmentClassNames.add(fragmentState.mClassName);
} else {
mFragmentArgs.add(null);
mFragmentTags.add(null);
mFragmentClassNames.add(null);
}
mFragments.add(null);
}
}
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) {
f.setMenuVisibility(false);
mFragments.set(index, f);
mFragmentArgs.set(index, f.mArguments);
mFragmentTags.set(index, f.mTag);
mFragmentClassNames.set(index, f.getClass().getName());
} else {
Log.w(TAG, "Bad fragment at key " + key);
}
}
}
// If restore will change
notifyDataSetChanged();
}
}
public static void fixActiveFragment(FragmentManager fragmentManager, Fragment fragment) {
FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
if (fm.mActive != null) {
int index = fragment.mIndex;
Fragment origin = fm.mActive.get(index);
if (origin != null) {
if ((origin.mIndex != fragment.mIndex) || !(origin.equals(fragment))) {
Log.e(TAG,
String.format("fixActiveFragment: Not Equal! Origin: %s %s, Fragment: %s $s",
origin.getClass().getName(), origin.mIndex,
fragment.getClass().getName(), fragment.mIndex
));
}
}
fm.mActive.set(index, fragment);
}
}
// Fix
// http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
#Override
public int getItemPosition(Object object) {
int index = mFragments.indexOf(object);
if (index < 0) {
return PagerAdapter.POSITION_NONE;
}
boolean isPositionChange = mTempPositionChange[index];
int result = PagerAdapter.POSITION_UNCHANGED;
if (isPositionChange) {
result = PagerAdapter.POSITION_NONE;
}
return result;
}
}
2020 now.
Simple add this to PageAdapter:
override fun getItemPosition(`object`: Any): Int {
return PagerAdapter.POSITION_NONE
}
You could just override the destroyItem method
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}
I hope this can help what you want.
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
#Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_UNCHANGED;
}
}
(If someone needs more information, or a better description let me know)
Hello i included the viewPagerLibrary from here: http://viewpagerindicator.com/#introduction today in my project.
No i get a really strange problem:
If i add a site or page (let's call it site in the next few lines) and remove it again everything is ok. But if i try to add a different page (Those pages are different Fragements which implements a BaseFragment class) the content of the first page is shown.
The same thing happens if i add a few pages and delete one inbetween those pages. The page which was after the deleted page shows now the deleted pages content.
An example of this bug:
The problem now is. If i add FragmentA after that FragmentB, then i delete FragmentA, FragmentB gets the view/content of FragmentA. The strange thing is, the object is the correct one (So the adapter return the correct object) and the Title is also the correct one.
In my main i create my Pager, Indicator and Adapter this way:
Cfg.mAdapter = new FragmentAdapter(getSupportFragmentManager());
Cfg.mPager = (ViewPager)findViewById(R.id.pager);
Cfg.mPager.setAdapter(Cfg.mAdapter);
Cfg.mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
Cfg.mIndicator.setViewPager(Cfg.mPager);
//We set this on the indicator, NOT the pager
Cfg.mIndicator.setOnPageChangeListener(TabHelper.onPageChangeListener);
(The Cfg is a static file to store those things for the usage)
My BaseFragment looks like the following:
public class BaseFragment extends Fragment{
public static int FILE_FRAGMENT = 0;
public static int FTP_FRAGMENT = 1;
public static int ADDFTP_FRAGMENT = 2;
public static int PREVIEW_FRAGMENT = 3;
public static int CSS_FRAGMENT = 4;
public static int BOOKS_FRAGMENT = 5;
public static int SNIPPETS_FRAGMENT = 6;
//private int id;
private int typ;
private String title;
public int getTyp() {
return typ;
}
public void setTyp(int typ) {
this.typ = typ;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
One of the Fragments looks like this (I think the other fragments make no difference):
public class FtpFragment extends BaseFragment {
private static RowLayout rowLayout_view;
public FtpFragment() {
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
init_data();
}
public static void init_data()
{
//Remove child for update
rowLayout_view.removeAllViews();
List<FtpData> ftps = FtpStorage.getInstance().getFtps();
if (ftps != null) {
for (FtpData f : ftps) {
View inflatedView;
inflatedView = View.inflate(Cfg.ctx, R.layout.ftp, null);
inflatedView.setOnClickListener(button_ftp_listener);
inflatedView.setOnLongClickListener(button_ftp_longClickListener);
inflatedView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, Converter.convertFromDPtoPixel(160.0f)));
inflatedView.setTag(f);
inflatedView.findViewById(R.id.book_imageview).setBackgroundDrawable(
Cfg.ctx.getResources().getDrawable(R.drawable.nopreview));
((TextView) inflatedView.findViewById(R.id.book_textview)).setText(f.nickname);
rowLayout_view.addView(inflatedView);
}
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_ftp, container, false);
rowLayout_view = (RowLayout) v.findViewById(R.id.rowLayout_ftps);
return v;
}
#Override
public String getTitle() {
return "FTPs";
}
#Override
public int getTyp() {
return BaseFragment.FTP_FRAGMENT;
}
#Override
public void setTyp(int typ) {
super.setTyp(typ);
}
}
To remove or add a page i call this:
public static void addNewTab(BaseFragment fragment)
{
Cfg.mAdapter.addItem(fragment);
Cfg.mPager.setCurrentItem(Cfg.mAdapter.getCount());
Cfg.mIndicator.notifyDataSetChanged();
}
public static void deleteActTab()
{
Cfg.mAdapter.removeItem(Cfg.mAdapter.getActPage());
Cfg.mIndicator.notifyDataSetChanged();
}
And that's the adapter:
public class FragmentAdapter extends FragmentPagerAdapter implements TitleProvider{
public List<BaseFragment> fragments = new LinkedList<BaseFragment>();
private int actPage;
public FragmentAdapter(FragmentManager fm) {
super(fm);
}
public void setActPage(int actPage) {
Lg.d("setActPage: " + actPage + " : " + fragments.get(actPage).toString());
this.actPage = actPage;
}
public void addItem(BaseFragment fragment)
{
Lg.d("addItem: " + fragment.toString());
fragments.add(fragment);
}
public void removeItem(int index)
{
if(index < getCount()){
Lg.d("RemoveItem: " + index + " : " + fragments.get(index).toString());
fragments.remove(index);
}
}
public BaseFragment getActFragment()
{
return getItem(getActPage());
}
public int getActPage() {
return actPage;
}
#Override
public BaseFragment getItem(int position) {
if(position < getCount())
{
Lg.v("getItem: " + fragments.get(position));
return fragments.get(position);
}
else
return null;
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public String getTitle(int position) {
Lg.v("Get Title: " + fragments.get(position).getTitle());
return fragments.get(position).getTitle();
}
}
Yeah i hope someone can help me.
If i forgot something let me konw.
Thanks in advance,
Mike
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
In your answer (by mikepenz), you don't need to set the adapter again. You may call the notifyDataSetChanged.
public static void deleteActTab(){
//We set this on the indicator, NOT the pager
int act = Cfg.mPager.getCurrentItem();
Cfg.mAdapter.removeItem(act);
if(act > 0)
{
Cfg.mPager.setCurrentItem(act-1);
Cfg.mIndicator.setCurrentItem(act-1);
}
//Also add conditions to check if there are any remaining fragments
Cfg.mIndicator.notifyDataSetChanged();
}
Did you consider using HashMap<Integer, Fragment> or ArrayAdapter<Fragment> to improve the performance or already using it?
Also, why do you use static method in BaseFragment? Please consider using MAT or logcat to check memory usage if these leak memory.
To avoid this problem you have to remove the specified fragment from backstack. Every time you delete a fragment from your list, it remains in backstack so the content will remain. Before deleting the fragment from your list you have to use FragmentTransaction to remove the page. Then the code might be something like this.
public void removePage(int currentPage) {
for (int i = pageFragmentList.size() - 1; i >= currentPage; i--) {
((MainActivity) context).getSupportFragmentManager().beginTransaction()
.remove(pageFragmentList.get(i)).commit();
}
pageFragmentList.remove(currentPage);
}
If you don't remove all the pages indexed after the current page, and only remove the current page from backstack, it might throw an exception.