10

I'm using a Viewpager with a the FragmentPagerAdapter to allow adding and removing of pages. Each page displays data obtained from the internet.

As a new page is added, a new Fragment is associated with that page. The data are obtained via AsyncTask and displayed in the Fragment. When the user chooses to remove a page, the idea is to destroy the page and the associated Fragment.

In general, this all works well. The problem I'm seeing is as follows:

  1. You have three pages with data:

    [Page 1] [Page 2] [Page 3]

  2. You delete any page other than the last one, say page 2; Page 2 disappears as desired:

    [Page 1] [Page 3]

  3. You add a new page; but instead of a blank, new page, the new page shows the data (view) from page 3.

    [Page 1] [Page 3] [Page 4, but showing view/data of Page 3, should be blank]


The page removal code in my activity is as follows:

   // Destroy fragment for this page
   DataListFragment curFrag = getFragmentByName(currentPage);
   FragmentManager fm = getSupportFragmentManager();
   fm.beginTransaction().remove(curFrag).commit();
   fm.executePendingTransactions();
   curFrag = null;

   // Remove page and update adapter
   mPageTitles.remove(position);        
   mAdapter.notifyDataSetChanged();

Using the debugger, it shows that the fragment is removed from the FragmentManager after the executePendingTransactions() call. But in the FrampePagerAdapters call, mAdapter.notifyDataSetChanged(), the fragment is added back in and then displayed when a new page is created.

I tried using the FrameStatePagerAdapter, since that should allow destroying fragments, but it did not work. In my FragmentPagerAdapter's getItemPosition() method, I use return FragmentAdapter.POSITION_NONE; as pointed out in another SO article I came across.

It seems as if the View for that page is not destroyed, but then added back into the new page. I tried using the removeViewAt() method on the view of the new page, but that did not work.


Being new to this, I'm sure I'm missing something obvious...

mraviator
  • 4,034
  • 9
  • 38
  • 51
  • I'm not sure that it's something obvious. I am running into this issue too. Did you resolve it? There may be an answer here: http://stackoverflow.com/questions/12510404/reorder-pages-in-fragmentstatepageradapter-using-getitempositionobject-object – Robert Karl Feb 01 '13 at 01:36
  • 1
    Can you show us your overrided method : public Fragment getItem(int position) – Gomino Jun 17 '13 at 23:43
  • When you are removing a fragment, you don't remove it from adapter. That's why it appears again. You need to remove it from your adapter (not view, but Item, that you use in getItem() method). the 4th fragment looks like third because it reuse fragment, like ListView's adapter. You have mistake in adapter logic. Show your adapter class – Alexander Mikhaylov Aug 16 '13 at 14:54

3 Answers3

1

You should rather extend FragmentStatePagerAdapter and remove corresponding item from the list of items in that adapter, instead of trying to remove a Fragment from activity. Do not forget to call adapter.notifyDataSetChanged() after you removed an item in adapter. Once done, ViewPager and FragmentStatePagerAdapter will take care for the rest.

sergej shafarenka
  • 20,071
  • 7
  • 67
  • 86
0

See your getCount() method is returning exact number of items in the viewPager. And yes, FragmentStatePagerAdapter counts too.

user2886615
  • 81
  • 1
  • 2
0

I've ended up with a solution that mixes the following knowledge based on experience:

  • You can add a new Fragment at the tail without problems.
  • You cannot readd a Fragment that was previously removed, as it leads to java.lang.IllegalStateException: Can't change tag of fragment sometimes, so you have to clone it.
  • For removing a Fragment you have to return PagerAdapter.POSITION_NONE in the method getItemPosition(Object object), and remove the Fragment from the FragmentManager.
  • If you are adding/removing/replacing in other place different than the tail, you have to remove everything from the position you are changing until the end, do the stuff, and then readd the (cloned) Fragments that you removed.

Here it is a complete FragmentActivity code with a FragmentPagerAdapter that has 3 methods for adding, removing and replacing tabs:

public class TabTestActivity extends FragmentActivity implements
        ActionBar.TabListener {
    private SectionsPagerAdapter mSectionsPagerAdapter;
    private ViewPager mViewPager;
    private static int tabCount = 0;
    private static String labelString = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        labelString = getString(R.string.title_section);
        setContentView(R.layout.activity_tab_test);
        final ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        mSectionsPagerAdapter = new SectionsPagerAdapter(
                getSupportFragmentManager());
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mSectionsPagerAdapter);
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
                    @Override
                    public void onPageSelected(final int position) {
                        (new Handler()).postDelayed(new Runnable() {

                            @Override
                            public void run() {
                                actionBar.setSelectedNavigationItem(position);
                            }

                        }, 1);
                    }
                });

        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
            actionBar.addTab(actionBar.newTab()
                    .setText(mSectionsPagerAdapter.getPageTitle(i))
                    .setTabListener(this));
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.tab_test, menu);
        return true;
    }

    public void addNewTab() {
        int position = (mSectionsPagerAdapter.getCount() > 0 ? mViewPager.getCurrentItem() : 0);
        mSectionsPagerAdapter.insertFragment(position);
        mViewPager.setCurrentItem(position, true);
    }

    public void removeTab() {
        if (mSectionsPagerAdapter.getCount() > 0) {
            int position = mViewPager.getCurrentItem();
            mSectionsPagerAdapter.removeFragment(position);
        }
    }

    public void replaceTab() {
        if (mSectionsPagerAdapter.getCount() > 0) {
            int position = mViewPager.getCurrentItem();
            mSectionsPagerAdapter.replaceFragment(position);            
            mViewPager.setCurrentItem(position, false);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.action_add_tab:
            addNewTab();
            return true;
        case R.id.action_remove_tab:
            removeTab();
            return true;
        case R.id.action_replace_tab:
            replaceTab();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onTabSelected(ActionBar.Tab tab,
            FragmentTransaction fragmentTransaction) {
        mViewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab,
            FragmentTransaction fragmentTransaction) {
    }

    @Override
    public void onTabReselected(ActionBar.Tab tab,
            FragmentTransaction fragmentTransaction) {
    }

    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        private List<Fragment> currentFragments;
        private FragmentManager fragmentManager;

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
            fragmentManager = fm;
            currentFragments = new ArrayList<Fragment>();
        }

        public void insertFragment(int position) {
            // Remove fragments from position
            List<Fragment> fragmentsToRemove = new ArrayList<Fragment>(currentFragments.subList(position, currentFragments.size()));
            int i = currentFragments.size() - 1;
            int j = -1;
            int k = i;
            while (i >= position) {
                currentFragments.remove(i);
                i--;
                j++;
            }
            notifyDataSetChanged();
            final ActionBar actionBar = getActionBar();
            while (k >= position) {
                actionBar.removeTabAt(k);
                k--;
            }
            android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
            while (j >= 0) {
                Fragment fragmentToRemove = fragmentsToRemove.get(j);
                transaction.detach(fragmentToRemove);
                transaction.remove(fragmentToRemove);
                j--;
            }
            transaction.commit();
            fragmentManager.executePendingTransactions();
            notifyDataSetChanged();
            // Add new fragment
            Fragment fragment = new DummySectionFragment();
            currentFragments.add(position, fragment);
            notifyDataSetChanged();
            actionBar.addTab(actionBar.newTab()
                    .setText(mSectionsPagerAdapter.getPageTitle(position))
                    .setTabListener(TabTestActivity.this), position);
            // Readd fragments
            if (fragmentsToRemove.size() > 0) {
                i = 1;
                for (Fragment fragmentToRemove : fragmentsToRemove) {
                    currentFragments.add(DummySectionFragment.cloneExistingFragment((DummySectionFragment)fragmentToRemove));
                    notifyDataSetChanged();
                    actionBar.addTab(actionBar.newTab()
                            .setText(mSectionsPagerAdapter.getPageTitle(position + i))
                            .setTabListener(TabTestActivity.this), position + i);
                    i++;
                }
            }
        }

        public void removeFragment(int position) {
            // Remove fragments from position
            List<Fragment> fragmentsToRemove = new ArrayList<Fragment>(currentFragments.subList(position, currentFragments.size()));
            int i = currentFragments.size() - 1;
            int j = -1;
            int k = i;
            while (i >= position) {
                currentFragments.remove(i);
                i--;
                j++;
            }
            notifyDataSetChanged();
            final ActionBar actionBar = getActionBar();
            while (k >= position) {
                actionBar.removeTabAt(k);
                k--;
            }
            android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
            while (j >= 0) {
                Fragment fragmentToRemove = fragmentsToRemove.get(j);
                transaction.detach(fragmentToRemove);
                transaction.remove(fragmentToRemove);
                j--;
            }
            transaction.commitAllowingStateLoss();
            fragmentManager.executePendingTransactions();
            notifyDataSetChanged();
            // Readd fragments (except one)
            if (fragmentsToRemove.size() > 1) {
                i = 0;
                for (Fragment fragment : fragmentsToRemove.subList(1, fragmentsToRemove.size())) {
                    currentFragments.add(DummySectionFragment.cloneExistingFragment((DummySectionFragment)fragment));
                    notifyDataSetChanged();
                    actionBar.addTab(actionBar.newTab()
                            .setText(mSectionsPagerAdapter.getPageTitle(position + i))
                            .setTabListener(TabTestActivity.this), position + i);
                    i++;
                }
            }
        }

        public void replaceFragment(int position) {
            // Remove fragments from position
            List<Fragment> fragmentsToRemove = new ArrayList<Fragment>(currentFragments.subList(position, currentFragments.size()));
            int i = currentFragments.size() - 1;
            int j = -1;
            int k = i;
            while (i >= position) {
                currentFragments.remove(i);
                i--;
                j++;
            }
            notifyDataSetChanged();
            final ActionBar actionBar = getActionBar();
            while (k >= position) {
                actionBar.removeTabAt(k);
                k--;
            }
            android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
            while (j >= 0) {
                Fragment fragmentToRemove = fragmentsToRemove.get(j);
                transaction.detach(fragmentToRemove);
                transaction.remove(fragmentToRemove);
                j--;
            }
            transaction.commit();
            fragmentManager.executePendingTransactions();
            notifyDataSetChanged();
            // Add new fragment
            Fragment fragment = new DummySectionFragment();
            currentFragments.add(position, fragment);
            notifyDataSetChanged();
            actionBar.addTab(actionBar.newTab()
                    .setText(mSectionsPagerAdapter.getPageTitle(position))
                    .setTabListener(TabTestActivity.this), position);
            // Readd fragments (except one)
            if (fragmentsToRemove.size() > 0) {
                i = 1;
                for (Fragment fragmentToRemove : fragmentsToRemove.subList(1, fragmentsToRemove.size())) {
                    currentFragments.add(DummySectionFragment.cloneExistingFragment((DummySectionFragment)fragmentToRemove));
                    notifyDataSetChanged();
                    actionBar.addTab(actionBar.newTab()
                            .setText(mSectionsPagerAdapter.getPageTitle(position + i))
                            .setTabListener(TabTestActivity.this), position + i);
                    i++;
                }
            }
        }

        @Override
        public Fragment getItem(int position) {
            if (currentFragments == null) {
                currentFragments = new ArrayList<Fragment>();
            }
            while (currentFragments.size() <= position) {
                currentFragments.add(null);
            }
            if (currentFragments.get(position) != null) {
                return currentFragments.get(position);
            }
            Fragment fragment = new DummySectionFragment();
            currentFragments.set(position, fragment);
            return fragment;
        }

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

        @Override
        public int getItemPosition(Object object) {
            int position = currentFragments.indexOf(object);
            if (position == -1) {
                return PagerAdapter.POSITION_NONE;
            }
            return position;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return ((DummySectionFragment)getItem(position)).getTitle();
        }
    }

    public static class DummySectionFragment extends Fragment {
        private int sectionNumber;

        public DummySectionFragment() {
            super();
            sectionNumber = ++tabCount;
        }

        public static DummySectionFragment cloneExistingFragment(DummySectionFragment fragment) {
            DummySectionFragment cloned = new DummySectionFragment();
            // Hack for avoiding autoincrement
            --tabCount;
            cloned.sectionNumber = fragment.getSectionNumber();
            return cloned;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_tab_test_dummy,
                    container, false);
            TextView dummyTextView = (TextView) rootView
                    .findViewById(R.id.section_label);
            dummyTextView.setText(String.format(labelString, sectionNumber));
            return rootView;
        }

        public int getSectionNumber() {
            return sectionNumber;
        }

        public String getTitle() {
            return String.format(labelString, sectionNumber);
        }
    }

}
Juan Sánchez
  • 980
  • 2
  • 10
  • 26