10

I have a problem with FragmentStatePageAdapter (that I use as adapter for a ViewPager) and the menu items from the action bar.

When I launch the application, everything is fine. If I move the task to background (for example, pressing HOME button) and I start doing stuff until the activity is ended, then when I go back to my application (through the launcher or a notification I create) everything is fine except there are duplicated menu items in the action bar.

An important detail is that the only duplicated items are those that are created in the onCreateOptionsMenu() of each fragment I use in the ViewPager.

If I replaceFragmentStatePageAdapter with FragmentPageAdapter the items are not duplicated anymore, but the fragments are not shown in the ViewPager (getItem() function from the adapter is never called so it does not return any fragment).

Any ideas? A way to avoid FragmentStatePageAdapter to duplicate menu items? Maybe use FragmentPageAdapter but with a modification to show the fragments? A modification in my fragments?

Here are some code snippets from my application...

How menu items are created inside the fragments:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    /* Create play button */
    final MenuItem mPlay = menu.add(R.string.play_all);

    mPlay.setIcon(R.drawable.ic_play_all);
    mPlay.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);

    mPlay.setOnMenuItemClickListener(new OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem item) {
            final List<Song> songs = getSongs();

            if (songs.size() == 0) {
                /* Show message */
                Toast.makeText(mContext, R.string.no_song_list, Toast.LENGTH_LONG).show();
            } else {
                /* Play song list */
                try { PlayManager.getService().playList(songs); } catch (Exception e) {}
            }

            return false;
        }
    });

    /* Create menu */
    super.onCreateOptionsMenu(menu, inflater);
}

How fragments are instantiated in the ViewPager adapter

@Override
public Fragment getItem(int position) {
    final Class<?> cls = mTabs.get(position);

    /* No tab */
    if (cls == null)
        return null;

    /* Instantiate fragment */
    final Fragment fragment = Fragment.instantiate(mContext, cls.getName(), null);

    /* Add to list */
    mFragments.put(position, fragment);

    /* Return fragment */
    return fragment;
}

Thanks!

PS: I tried to change the launchMode of the activity to "singleTop" and I also tried to return in getItem() the previosly created fragment (but that's useless as getItem() is never called when I return to the application, as I said before).

Miguel Botón
  • 766
  • 5
  • 20

4 Answers4

3

I found a solution to my problem a few days after posting my problem. I'm sorry for putting it here so late.

The following code fixed my problem:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    outState.remove("android:support:fragments");
}

As simple as writing that piece of code in the activity where I instantiate and use the fragments.

I hope this helps :)

Miguel Botón
  • 766
  • 5
  • 20
  • Awesome! :) Another solution: call setRetainInstance(true) on fragments in pager. – HighFlyer Sep 19 '12 at 07:11
  • I will have to try that one too :) – Miguel Botón Sep 19 '12 at 16:01
  • After some experiments its seems that outState.remove does not works with screen rotation, because FragmentStatePageAdapter uses this information when state is restoring. Also setRetainInstance(true) does not works with launching application from background. – HighFlyer Sep 23 '12 at 13:26
  • @HighFlyer Where to put setRetainInstance(true) in pager?? – Roon13 Dec 17 '15 at 17:49
  • @Roon13 You setRetainInstance is a method of Fragment. You may call in in onCreate method of Fragment. – HighFlyer Dec 18 '15 at 11:39
  • @HighFlyer My other fragments onOptionsItemSelected method is not getting called. It is showing data from previous fragments. Please help. – Roon13 Dec 18 '15 at 20:13
  • @Roon13 do you calls setHasOptionsMenu? – HighFlyer Dec 22 '15 at 07:31
  • 1
    Yes. Please check my problem http://stackoverflow.com/questions/34399646/fragment-methods-are-not-getting-called – Roon13 Dec 22 '15 at 08:46
3

Before inflating the options menu in your Fragment inside the ViewPager, check whether the Fragment instance is visible to prevent duplicated menu items:

// This should be in your Fragment implementation
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
    if(isVisible())
    {
        inflater.inflate(R.menu.menu_fragment, menu);
    }
    super.onCreateOptionsMenu(menu, inflater);
}
Márton
  • 473
  • 7
  • 13
2

I assume the problem is that the FragmentStatePagerAdapter creates a new Fragment each time you swipe. As a result, onCreateOptionsMenu() is called for each Fragment that is created.

There are, I think, two solutions:

  1. Take the action bar manipulation out of the Fragments. Instead of setting an onclick for the menu item inside of the Fragment, you send a message to the current Fragment from the Activity.

  2. Use Menu.findItem() to see if another Fragment has already added the MenuItem you want, and if so attach the current Fragment to it (in onPrepareOptionsMenu()).

Dan Lew
  • 85,990
  • 32
  • 182
  • 176
  • That's not the reason. The first time I launch the app, and when I swipe, it works perfect. When I swipe, the items created by the fragment I move from are deleted, and the items from the fragment I move to are added. The problem only happens when the activity is closed while being on background and I start it again. – Miguel Botón May 07 '12 at 17:07
0

I found some solution try it may be work

In your duplicate menu fragment Add menu.clear() before inflate menu file in onCreateOptionsMenu(Menu menu, MenuInflater inflater)

@Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        menu.clear();
        inflater.inflate(R.menu.main,menu);
    }
Mansukh Ahir
  • 3,373
  • 5
  • 40
  • 66
  • My answer here might be helpful if you don't want to clear your whole menu, like if you have items added from the Activity: https://stackoverflow.com/a/63008005/5916188 – Sam Jul 21 '20 at 05:38