I've tried to find an answer about this but with no luck. I have a fragment that has an a menu item called 'menu_roi_result_calc'. Every time there is a screen rotation, a new menu item is created. The code is shown below:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_roi_result_calc, menu);
return true;
}
However, after a couple of screen rotations, this is what I get:
I get the feeling this is due to the fact that the menu items are being recreated at every rotation, therefore adding a new item every time a rotation occurs. How do I get this to stop? How can I check if the item is there and prevent from recreating it again? Any code example would be greatly appreciated.
You must clear your menu object before adding items. I had the same problem and this was the best solution that I've found.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.menu_roi_result_calc, menu);
super.onCreateOptionsMenu(menu, inflater);
}
I found out what the issue was. I was not reusing the original tagged fragments. All I had to do was make sure to check savedInstanceState and check if a tagged fragment had already been created... if so reuse the same fragment instead of creating a new one.
as workaround you could clear the menu, before inflating it again, calling clear
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.clear();
getMenuInflater().inflate(R.menu.menu_roi_result_calc, menu);
return true;
}
it removes all entries from the menu, leaving it as it had just been created
When you rotate your application your activity is destroyed and a new one is created. If you are attaching your fragment to the activity where it is being created than it will have the old one attached and will attach a new one. To avoid this I would do something like below. This will remove all of the current fragments and host whatever yours is.
FragmentManager fm = new FragmentManager();
for(Fragment aFrag : fm.getFragments()) {
fm.beginTransaction().remove(aFrag).commit();
}
fragment = createFragment(new Fragment());
fm.beginTransaction().add(R.id.YouGetThePicture, fragment).commit();
Related
I am creating android application and I'm trying to respect as much as possible the latest Android usability standards. In particular, I am preparing the user interface using the navigation drawer, and I'm trying to ensure compatibility with 2.1+ Android versions. To appreciate the problem, the project consists of:
A main activity;
A navigation drawer;
Four fragments (with their associated layouts).
The problem I'm experiencing occurs when opening the navigation drawer: although each Fragment has its specific menu, when I open the navigation drawer it is added to the nav drawer menu. I tried in several ways (invalidateOptionMenu(), menu.clear(), manipulating functions isDrawerOpen() and isDrawerClose() and more), but I cannot remove the Fragment's menu when opening the navigationdrawer.
These are some snippets of my code, much of it generated by Android Studio, the IDE I'm using:
In main activity:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mNavigationDrawerFragment.isDrawerOpen()) {
// Only show items in the action bar relevant to this screen
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
getMenuInflater().inflate(R.menu.global, menu);
restoreActionBar();
return true;
}
return super.onCreateOptionsMenu(menu);
}
where "global" is a simple menu with a classical "ic_action_overflow".
And in my fragments I've:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment1, menu);
}
(It's the same of the other Fragments).
Someone can give me some advice on how to act?
I faced the same problem using the boilerplate code generated by Android Studio and got it working by modifying the menu in NavigationDrawerFragment.onPrepareOptionsMenu() (in my case, I wanted to clear the menu altogether):
#Override
public void onPrepareOptionsMenu(Menu menu) {
if (mDrawerLayout != null && isDrawerOpen()) {
menu.clear();
}
}
This is roughly how the options menu is recreated:
NavigationDrawerFragment, which is generated by the IDE, calls supportInvalidateOptionsMenu() when the drawer is opened or closed.
onCreateOptionsMenu gets called: The hosting activity and each of the added fragments gets a chance to contribute menu items.
onPrepareOptionsMenu gets called: Again, the hosting activity and each of the added fragments get a chance to modify the menu.
The fragments are iterated in the order they were added. There is no way to stop the chain of calls midway in steps 2. and 3.
So the idea is to let NavigationDrawerFragment do last-minute changes to the menu in its onPrepareOptionsMenu and no other fragments.
If you need to let other fragments do something in onPrepareOptionsMenu, then you may have to setup those other fragments so they can determine if the drawer is open or not and change their behavior accordingly. This may mean perhaps adding a isDrawerOpen method to the hosting activity or passing in the drawer identifiers to the fragment like it's done in NavigationDrawerFragment.setup().
In your fragment onCreate, add this :
setHasOptionsMenu (true);
And then hide through onPrepareOptionsMenu. e.g.
#Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.findItem(R.id.action_settings).setVisible(false);
}
If you've implemented the navigation drawer the way Android Studio sets it up for you in its example code with a NavigationDrawerFragment, you should have two xml to start with main.xml (activity wide actionbar menu items) and global.xml (global items). I then added a fragment specific menu which adds items to the "activity menu items" as long as the drawer is closed...
Activity:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mNavigationDrawerFragment.isDrawerOpen()) {
// Only show items in the action bar relevant to this activity
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
getMenuInflater().inflate(R.menu.main, menu);
this.menu = menu;
restoreActionBar();
return true;
}
return super.onCreateOptionsMenu(menu);
}
NavigationDrawerFragment
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// If the drawer is open, show the global app actions in the action bar.
// See also showGlobalContextActionBar, which controls the top-left area
// of the action bar.
if (mDrawerLayout != null && isDrawerOpen()) {
inflater.inflate(R.menu.global, menu);
showGlobalContextActionBar();
}
super.onCreateOptionsMenu(menu, inflater);
}
and in your fragments add as you've described above
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Add fragment specific action bar items to activity action bar items.
// Activity wide items are in main.xml
// Visible action bar items if navigation drawer is visible/open
// are in global.xml
super.onCreateOptionsMenu(menu, inflater);
if (!mNavigationDrawerFragment.isDrawerOpen()) {
inflater.inflate(R.menu.fragment_menu, menu);
}
}
Check out this answer here: https://stackoverflow.com/a/18135409/2558344. Its basically using a boolean to clear items in the navigation drawer and vice versa. But alternatively, you could declare Menu menu as a private variable in your class and use it as: onCreateOptionsMenu(menu, MenuInflater inflater).
Then check in your fragments onStop(), onPause() if it's displaying items or not, if yes then clear them, like:
if (menu != null)
menu.clear();
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!
Perhaps I'm missing the obvious, but my menu is only been created and populated when onCreateOptionsMenu(Menu menu) is called, as in this example, and this only seem to be called after I press the menu button.
I would like to populate the menu upon the creation of the activity, how can achieve that?
UPDATE:
Perhaps a better question would be:
How can I get the Menu instance of my Activity?
Thanks
Create a class which holds those states then set your enabled/checked etc from the properties of that class in onCreateOptionsMenu()
class MenuStates{
public static boolean userDidPressTheButton;
public static boolean serverDidRespond;
public static boolean colorWasChanged;
}
void someEventHandler(){
MenuStates.userDidPressTheButton = true;
}
void onCreateOptionsMenu(){
myCheckBox.setChecked(MenuStates.userDidPressTheButton);
}
[EDIT]
You don't say why you want to get the menu instance. One approach:
Menu optsMenu;
...
// this is called once only before the end of the Activity onCreate().
onCreateOptionsMenu(Menu menu){
opstMenu = menu;
super.onCreateOptionsMenu(menu);
}
Following that, modify your menu as you wish. Do any "as it pops" up work in onPrepareOptionsMenu().
The key understanding is the difference between onCreateOptionsMenu() and onPrepareOptionsMenu().
[MORE EDIT]
To completely control the thing yourself:
Menu optsMenu;
onCreate(){
openOptionsMenu() // the menu won't show in onCreate but onCreateOptionsMenu is shown
closeOptionsMenu()
}
onCreateOptionsMenu(Menu menu){
optsMenu = menu;
}
onPrepareOptionsMenu(Menu menu){
menu.clear();
for (int i=0;ioptsMenu.size();i++){
menu.add(optsMenu.get(i).getTitle());
}
return super.onPrepareOptionsMenu(menu);
}
From 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.
http://developer.android.com/reference/android/app/Activity.html#onCreateOptionsMenu%28android.view.Menu%29
In some methods of my Activity I want to check the title of menu or know if it is checked or not. How can I get Activity's menu. I need something like this.getMenu()
Be wary of invalidateOptionsMenu(). It recreates the entire menu. This has a lot of overhead and will reset embedded components like the SearchView. It took me quite a while to track down why my SearchView would "randomly" close.
I ended up capturing the menu as posted by Dark and then call onPrepareOptionsMenu(Menu) as necessary. This met my requirement without an nasty side effects. Gotcha: Make sure to do a null check in case you call onPrepareOptionsMenu() before the menu is created. I did this as below:
private Menu mOptionsMenu;
#Override
public boolean onCreateOptionsMenu(final Menu menu) {
mOptionsMenu = menu
...
}
private void updateOptionsMenu() {
if (mOptionsMenu != null) {
onPrepareOptionsMenu(mOptionsMenu);
}
}
Call invalidateOptionsMenu() instead of passing menu object around.
you could do it by passing the Menu object to your Activity class
public class MainActivity extends Activity
{
...
...
private Menu _menu = null;
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
_menu = menu;
return true;
}
private Menu getMenu()
{
//use it like this
return _menu;
}
}
There several callback methods that provide menu as a parameter.
You might wanna manipulate it there.
For example:
onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
onCreateOptionsMenu(Menu menu)
onCreatePanelMenu(int featureId, Menu menu)
There several more, best you take a look in activity documentation and look for your desired method:
http://developer.android.com/reference/android/app/Activity.html
As far as I could understand what you want here is which may help you:
1. Refer this tutorial over option menu.
2. Every time user presses menu button you can check it's title thru getTitle().
3. Or if you want to know the last menu item checked or selected when user has not pressed the menu button then you need to store the preferences when user presses.
Android now has the Toolbar widget which was a Menu you can set/get. Set the Toolbar in your Activity with some variation of setSupportActionBar(Toolbar) for stuff like onCreateOptionsMenu from a Fragment for example. Thread revived!
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);
}