FragmentStatePageAdapter duplicates ActionBar menu items - android

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).

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 :)

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);
}

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:
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.
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()).

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);
}

Related

Working with OptionsMenu in nested fragment not updating

I displayed a fragment A that implements a ViewPager with several fragments (nested fragments).
In my nested fragments, I inflate a menu with the following method.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.my_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
This question was already asked here. And i tried all the answers its not working.
My issue is
Everything working fine.but when i open another fragment(it have not any option menu) and get back to previous view pager fragment while clicking menu item onOptionsItemSelected not firing. When i swipe fragment in viewpager and come back to the previous one, when i click menu item its firing.
Its because viewpager maintain 3 fragment alive at a time. so when you come back, it set menu visibility status true to last fragment. thats why your menu item click not firing.
Use the following in the fragment where you keeping a viewpager in your case fragment A.
private boolean isInitial=true;
#Override
public void onResume() {
super.onResume();
if (!isInitial) {
int pos = viewpager.getCurrentItem();
if (pageAdapter.getItem(pos).getUserVisibleHint() && pageAdapter.getItem(pos).isVisible()) {
pageAdapter.getItem(pos).setMenuVisibility(true);
}
} else {
isInitial = false;
}
}

Duplicate Menu and Data in Fragments after using FragmentStatePagerAdapter

I tried so many answers provided by various posts here but nothing worked for me.
Problem- I have navigation drawer that has 6 fragments but single activity. Everything worked fine till I changed 1st ranked fragment in drawer. I wanted Swipe tabs inside first fragment. So I used FragmentStatePagerAdapter.
Each fragment has its own menu along with MainActivity Menu.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Notify the system to allow an options menu for this fragment.
setHasOptionsMenu(true);
}
And inflated like this:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.story, menu);
}
Everything works fine. But When I visit other fragments in navigation drawer then it shows duplicate menu in toolbar. It creates more duplicates if there is space left in toolbar when I visit other fragments.
Try 1 : To solve this problem I initially used:
#Override
public void onPrepareOptionsMenu(Menu menu) {
menu.clear();
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.story, menu);
}
With this I don't get duplicate menu but now I don't see MainActivity menus.
Try 2:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
getActivity().invalidateOptionsMenu();
inflater.inflate(R.menu.story, menu);
}
With this I get both Fragment and Activity menu but Duplicates are there.
This should be easy to solve but I am not picking up a way to deal with this. Maybe I didn't understand the life cycle well?
My other approach- Implementing all menus in Fragments will do the trick but this should be our last option.
Solution to this - To maintain both Menu all I have to do is this (Very easy solution):
menu.clear();
inflater.inflate(R.menu.story, menu);
getActivity().getMenuInflater().inflate(R.menu.main, menu);
Problem 2 OnOptionsItemSelected method from 1st fragment is getting called in other fragments.
private void hideAllMenuItems() {
if (actionBarMenu != null) {
actionBarMenu.findItem(R.id.action_item1).setVisible(false);
actionBarMenu.findItem(R.id.action_item2).setVisible(false);
}
}
private void showMenuIcon() {
if (actionBarMenu != null) {
hideAllMenuItems();
if (currentFragment instanceof Fragment1)
actionBarMenu.findItem(R.id.action_item1).setVisible(true);
else if (currentFragment instanceof Fragment2)
actionBarMenu.findItem(R.id.action_item2).setVisible(true);
}
}
call shoeMenuIcon() each time new fragment load..
Hope you are looking for this

OptionsMenu of Fragments in Viewpager showing each other's Buttons

I've got three fragments in a viewpager.
Two of these fragments have their own version of the onCreateOptionsMenu method:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// Set up 1 action button
inflater.inflate(R.menu.home_snapshot_add, menu);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// Set up 2 action buttons
inflater.inflate(R.menu.home_snapshot_send, menu);
}
The home activity has a basic onCreateOptionsMenu method:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
return false;
}
In the onCreate method, each fragment calls the method:
setHasOptionsMenu(true);
Each of the menu items have the tag:
android:showAsAction="always"
Seems like as I open the Activity, all three buttons appear.
However, when I scroll through them, the wrong ones magically disappear.
It feels like the activity is calling every Fragment's options menu on Activity creation and then changes the menu appropriately when I swipe left and right.
I've checked the menus but not sure what's wrong.
Anything you reckon I need to check? I'm a little out of ideas.
Thanks!
In your ViewPager's OnPageChangeListener and after setting the adapter to the ViewPager, have this:
#Override
public void onPageSelected(int position){
invalidateFragmentMenus(position);
}
private void invalidateFragmentMenus(int position){
for(int i = 0; i < mViewPagerFragentAdapter.getCount(); i++){
mViewPagerAdapter.getItem(i).setHasOptionsMenu(i == position);
}
invalidateOptionsMenu(); //or respectively its support method.
}
After setting your fragment adapter call the same method with following argument:
invalidateFragmentMenus(mViewPager.getCurrentItem());
The above statements will prevent all other fragments not to receive call on onCreateOptionsMenu() method when invalidateOptionsMenu() is called, only the currently visible fragment will receive and be able to populate the options menu.
I've used this and it has worked for me:
//In your Fragment
#Override
public void onResume() {
super.onResume();
setHasOptionsMenu(isVisible());
}

Access Fragment's Menu within a FragmentStatePagerAdapter

I use a FragmentStatePagerAdapter to display fragments inside a ViewPager.
These fragments display 2 additional icons on top of the ones coming from the parent Activity.
These 2 icons are showed/hidden according to fragment displayed, so I tried to keep the Menu inflated in memory in order to modify it dynamically.
On my ViewPager, 3 fragments are always loaded, only the middle one is displayed.
The problem is the following: it seems that onCreateOptionsMenu and onPrepareOptionsMenu are called only for the first Fragment created, because of this, I get null pointers when I try to get the menu on the other fragments. Please see the code below:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
//Get the 2 icons for the rates
inflater.inflate(R.menu.rate_fragment, menu);
//Get activity's icons
super.onCreateOptionsMenu(menu, inflater);
mMenu = menu;
}
Here are the functions I use to hide/show the menu items:
private void hideOption(int id){
MenuItem item = mMenu.findItem(id);
if(item != null){
item.setVisible(false);
}
}
private void showOption(int id){
MenuItem item = mMenu.findItem(id);
if(item != null){
item.setVisible(true);
}
}
But these functions fail as soon as the system tries to load the second Fragment.
My guess is that the menu is only created once for the FragmentStatePagerAdapter, but then how can I modify dynamically the menu? Do I have to modify it using the Activity?
Thanks!

Interaction between Activity and Fragments

In my application the main activity hosts two fragments and attached to acticity as ActionBarTabs. using the following code. NOTE: activity and the 2 fragments are defined in seperate xml layout files (See the picture at the bottom)
private void createActionTabs() {
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
//Status Tab
String strStatus = getResources().getString(R.string.lblStatus);
ActionBar.Tab statusTab = actionBar.newTab();
statusTab.setText(strStatus);
TabListener<SFrag> sFTabListener = new TabListener<SFrag>(this, strStatus, SFrag.class);
statusTab.setTabListener(sFTabListener);
actionBar.addTab(statusTab);
//Controller Tab attached the same way
.....
}
The ActionBar Items (start and refresh) are added using
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return true;
}
Now coming to my problem:
I want to update the data shown on the Status Fragment at application load, fragment resume and click of refresh menu item from the action bar. Now when I try to access the Status fragment from Main Activity using the following code
SFrag frag = (SFrag) getFragmentManager().findFragmentById(R.id.st_frag);
Log.d(TAG, "In Activity SFrag is " + (frag == null ? "null" : "not null"));
if (frag != null) {
//calls the method to update data
fragment.updateStatusData(statusInformation);
}
The getFragmentManager().findFragmentById methods always returns null. I even tried the onResume method of the activity, the fragment objects is still returned as null. So how do I access the fragment and thus accessa method of that fragment from host acticty.
Secondly, I am trying to use the action_service (its shown as Start button just for this mockup) in action bar as a toggle for satrting or stopping a background service. I can easily update the title/icon for start menu item from onOptionsItemSelected method (I save the current status running/stoppede in shared-preferences). but when I try to accees the menuItem at onStart/onResume of the activity by using
MenuItem mi = (MenuItem) findViewById (R.id.action_service);
it always returns null. So How Can I access action_service menu Item in onResume/onStart to update it.
My Application looks like this
First of all if you want to declare MenuItem, you should do it in this way :
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.main_activity, menu);
MenuItem mRefresh = menu.findItem(R.id.refresh_menu_item);
return true;
}
About updating your Fragment on applications load or Activity's start, just set selected the tab which holds your Fragment and put the code which will load the data in your Fragment on it's onStart() or onActivityCreated() method. You can override onOptionsItemSelected() in your Fragment, so you can update your views not from your Activity which holds your Fragment, but from it's own class.
Edit: Here is an example how you should handle menu from your Fragment :
public class MyFragment extends Fragment {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true); // Do not forget this!!!
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.first_fragment, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.action_refresh:
// Do You stuff here
break;
}
return super.onOptionsItemSelected(item);
}
}
Using this you can add and use MenuItem's in your Fragment.
Hope this helps!
I can answer the MenuItem part just not the other part.
for the menu item as per the docs You can safely hold on to menu (and any items created from it), making modifications to it as desired, until the next time onCreateOptionsMenu() is called.
for example
#Override
public boolean onCreateOptionsMenu(Menu menu) {
actionMenu = menu;
return true;
}
so basically anytime you want to change one of the items you can do this
MenuItem item = actionMenu.getItem(0);

Categories

Resources