Viewpager: if the page is deleted, the content of the following pages gets the content of the deleted pages

(If anyone needs more information or a better description, let me know)

Hello, I have included viewPagerLibrary here: http://viewpagerindicator.com/#introduction today in my project.

No, I have a really strange problem: If I add a site or page (let me call it a site in the next few lines) and delete it again, everything will be okay. But if I try to add another page (these pages are different from Fragements, which implements the BaseFragment class), the contents of the first page are displayed. The same thing happens if I add several pages and delete one of them between these pages. The page after the deleted page displays the contents of the deleted pages.

An example of this error: Now the problem. If I add FragmentA after this FragmentB, then I delete FragmentA, FragmentB will get the view / contents of FragmentA. The strange thing is: the object is correct (therefore, the adapter returns the correct object), and the Title is also correct.

In my main part, I create my pager, indicator and adapter in 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); 

(Cfg is a static file to store these things for use)

My BaseFragment looks like this:

 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 don't matter):

 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 delete 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 what 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(); } } 

Yes, I hope someone can help me.

If I forgot something, let me understand.

Thanks in advance, Mike

+6
source share
3 answers

Ok, now I solved my problem in a hacky way, but yes, it works;). If someone can improve my decision, please let me know. For my new solution, I now use CustomFragmentStatePagerAdapter, but it does not save the state as it should, and saves all the fragments in the list. This can cause a memory problem if the user has more than 50 fragments, for example, the usual FragmentPagerAdapter. It would be great if someone could add state to my solution without deleting my corrections. Thanks.

So here is 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 is my regular 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 so I delete the fragment. (I know this a little more than just .remove ()). To be free to improve your decision, you can also add this code somewhere in the adapter, so yes. It depends on the user who is trying to implement this. I use this in my TabHelper.java (a class that 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 Cfg. thing. I keep a reference to these objects in the cfg class, so I can always use them without the need for a special Factory.java ...

Yes. Hope I could help. Feel free to improve this, but let me know so that I can improve my code.

Thanks.

If I missed any code, let me know.


My old answer also works, but only if you have different fragments. FileFragment, WebFragment, ... No, if you use one of these fragments twice.

I got a pseudo-job now. This is a really dirty decision, and I'm still looking for the best. Please help.

I changed the code where I delete the tab:

  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 anyone can improve this code let me know. If someone can tell us a real answer to this problem. please add it here. Many people face this problem. I have added a reputation of 50 for those who solve it. I can also give a donation to the one who solves it.

thanks

+2
source

In your answer (by mikepenz) you do not need to install the adapter again. You can call 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(); } 

Do you consider using a HashMap<Integer, Fragment> or ArrayAdapter<Fragment> to improve performance or already using it? Also, why are you using a static method in BaseFragment? Please consider using MAT or logcat to check memory usage if this memory leak.

0
source

To avoid this problem, you need to remove the specified fragment from the backstack . Each time you remove a fragment from your list, it remains in the backstack , so the contents will remain. Before removing a fragment from the list, you must use FragmentTransaction to remove the page. Then the code could 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 do not delete all pages indexed after the current page and only delete the current page from backstack , this may throw an exception.

0
source

Source: https://habr.com/ru/post/907175/


All Articles