10

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?

Sufian
  • 6,405
  • 16
  • 66
  • 120
user3319400
  • 823
  • 2
  • 7
  • 10

4 Answers4

12

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:

  1. NavigationDrawerFragment, which is generated by the IDE, calls supportInvalidateOptionsMenu() when the drawer is opened or closed.
  2. onCreateOptionsMenu gets called: The hosting activity and each of the added fragments gets a chance to contribute menu items.
  3. 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().

ento
  • 5,801
  • 6
  • 51
  • 69
  • Thanks for that. I found that `onPrepareOptionsMenu` works better than `onCreateOptionsMenu`. `onCreateOptionsMenu` did nothing with my ActionBar, whereas the same code in `onPrepareOptionsMenu` did gave the desired effect. Perhaps this is because it's a Samsung S4... x| – ar34z Aug 28 '14 at 08:40
  • @LucasTan There is no clean way, as far as a I know. It worked in my case because no other fragments implemented `onPrepareOptionsMenu`. I've updated my answer to address such cases. – ento Nov 05 '14 at 01:21
  • Isn't this the right answer? That worked for me except for a small delay to hide button which is caused by the animated slide in of drawer fragment. – Ajith M A Mar 05 '15 at 13:31
5

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);
    }
user2661518
  • 2,677
  • 9
  • 42
  • 79
0

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();
Community
  • 1
  • 1
Aashir
  • 2,621
  • 4
  • 21
  • 37
  • Thank you for your response! I've tried using the solution on your link, but I give an error on "public boolean onPrepareOptionsMenu(Menu menu)" and in return value, because it expects a void value. I've also tried with your solution but I don't understand completely. I've added a private menu variable in my MainActivity.java but the variable is never used. I've update my onStop() and onPause methods too. But what should I do with onCreateOptionMenu() method? Thank you very much indeed! – user3319400 Feb 17 '14 at 15:09
  • Do you see the "Menu menu" in onCreateOptionsMenu(...), yeah replace both with "menu" which refers to your local variable. And no need to call onPrepareOptionsMenu(...) – Aashir Feb 17 '14 at 16:57
  • How does one call onCreateOptionsMenu() again to recreate the menu when I want it shown again ? – Someone Somewhere Dec 09 '14 at 23:54
0

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);
    }
}
JJD
  • 50,076
  • 60
  • 203
  • 339
Patrick
  • 31
  • 2