onCreateOptionsMenu() in fragment not replacing ActionBar menus

21,991

Solution 1

Try putting setHasOptionsMenu(true); inside the onCreateView()of your ChildFragment.java I don't see one in the code you posted.

Solution 2

I got it working! The problem I had was that I added a custom drop down menu to the main fragment. So each time I change the fragment through the drawer navigation I manually have to remove that drop down menu and when returning to the main fragment I will re-add it.

Here is what I did:

In the parent activity:

MainActivity.java:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.signin_menu, menu);

    getMenuInflater().inflate(R.menu.clear_search_history_menu, menu);

    // Search
    // Associate searchable configuration with the SearchView
    getMenuInflater().inflate(R.menu.search_menu, menu);
    SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
    searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));

    return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    menu.clear();
    getMenuInflater().inflate(R.menu.signin_menu, menu);
    getMenuInflater().inflate(R.menu.clear_search_history_menu, menu);

    // Search
    // Associate searchable configuration with the SearchView
    getMenuInflater().inflate(R.menu.search_menu, menu);
    SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
    searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));

    // If the nav drawer is open, hide action items related to the content
    // view
    boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
    menu.findItem(R.id.search).setVisible(!drawerOpen);

    return super.onPrepareOptionsMenu(menu);
}

// This is the selection of the drawer (stripped some code to keep it short)
private void selectItem(int position) {
    Fragment fragment = null;
    Bundle args = new Bundle();
    boolean isFragment = false;

    ActionBar actionBar = getActionBar();

    switch (position) {
    case DRAWER_MAIN:
        fragment = new WelcomeSectionFragment();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
        actionBar.setListNavigationCallbacks(new GameSystemsAdapter(getActionBarThemedContextCompat(), allSystemsPlusEmpty), this);
        actionBar.setTitle(R.string.app_name);
        isFragment = true;
        break;
    case DRAWER_FAVORITES:
        fragment = new FavoritesMainFragment();
        // Remove System-Select Drop-Down
        actionBar.setListNavigationCallbacks(null, null);
        actionBar.setNavigationMode(0);
        actionBar.setTitle(R.string.favorites);
        isFragment = true;
        break;
    case DRAWER_TOP_MEMBERS:
        fragment = new TopMembersFragment();            
        // Remove System-Select Drop-Down
        actionBar.setListNavigationCallbacks(null, null);
        actionBar.setNavigationMode(0);
        actionBar.setTitle(R.string.top_members_top_helping);
        isFragment = true;
        break;
    default:
        break;
    }

    if (isFragment) {
        fragment.setArguments(args);
        FragmentManager frgManager = getFragmentManager();
        frgManager.beginTransaction().replace(R.id.content_frame, fragment).commit();

        mDrawerList.setItemChecked(position, true);
        setTitle(dataList.get(position).getItemName());
        mDrawerLayout.closeDrawer(mDrawerList);
    }

}

and here the part of the fragment(s):

ChildFragment.java

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);

    if (Build.VERSION.SDK_INT >= 11) {
        selectMenu(menu);
    }
}

@Override
public void onPrepareOptionsMenu(Menu menu) {
    selectMenu(menu);
}

private void selectMenu(Menu menu) {
    parentActivity.getMenuInflater().inflate(R.menu.contactform_send_menu, menu);
}

If there are any better solutions I'm very interested in your ideas.

Solution 3

There are two cases to consider:

  1. You want all action items to come from within fragments exclusively.
  2. You also have action items that should be global and are defined within the activity.

Let's begin with option 2 since it's the most common case.

If you have fragment specific items and global items at the same time, you do not want to use onCreateOptionsMenu() within the fragment. The reason is that it would look weird - even if it worked. The fragment actions would be added right after creating the fragment, while the global items would be added after the drawer has closed (by default). I think no user would like that.

What you could do to still let the fragment decide which items to display is to create an interface for all fragments to implement. This could define one method which would return the menu id for the menu to inflate or -1 if no menu should be inflated.

In case of option 1, I can only assume that you clear() the menu within the Activity's onCreateOptionsMenu(). Otherwise it wouldn't delete the fragments' menu entries. So just get rid of the clear() and all should be fine.

Share:
21,991
Dominik
Author by

Dominik

Updated on February 25, 2020

Comments

  • Dominik
    Dominik about 4 years

    I'm using the NavigationDrawer in my app and replace the fragment when clicking a item in the drawer. My problem I have is that the menu items in the ActionBar are not updated when I change the fragment.

    I've followed this tutorial https://www.grokkingandroid.com/adding-action-items-from-within-fragments/ closely but it's still not working in my app.

    Adding some code snippets of the parent activity and one of the Fragments here. What I need is to display the other menu items of the contact form fragment (R.menu.contactform_send_menu) when the initial fragment gets replaced with ContactFormFragment.java.

    public class MainActivity extends FragmentActivity implements ActionBar.OnNavigationListener {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            setTitle(R.string.app_name);
    
            // Set up the action bar to show a dropdown list.
            ActionBar actionBar = getActionBar();
            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
            actionBar.setDisplayShowTitleEnabled(true);
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setHomeButtonEnabled(true);
    
            createNavigationDrawer(savedInstanceState);
        }
    
        private final int DRAWER_MAIN = 0;
        private final int DRAWER_CONTACT = 5;
    
        // update the main content by replacing fragments
        private void selectItem(int position) {
            Fragment fragment = null;
            Bundle args = new Bundle();
            boolean isFragment = false;
    
            switch (position) {
            case DRAWER_MAIN:
                fragment = new WelcomeSectionFragment();
                args.putString(WelcomeSectionFragment.ITEM_NAME, dataList.get(position).getItemName());
                args.putInt(WelcomeSectionFragment.IMAGE_RESOURCE_ID, dataList.get(position).getImgResID());
                getActionBar().setTitle(R.string.app_name);
                isFragment = true;
                break;
            case DRAWER_CONTACT:
                fragment = new ContactFormFragment();
                args.putString(ContactFormFragment.ITEM_NAME, dataList.get(position).getItemName());
                args.putInt(ContactFormFragment.IMAGE_RESOURCE_ID, dataList.get(position).getImgResID());
                getActionBar().setTitle(R.string.contactform_title);
                isFragment = true;
                break;
            default:
                break;
            }
    
            if (isFragment) {
                fragment.setArguments(args);
                FragmentManager frgManager = getFragmentManager();
                frgManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
    
                mDrawerList.setItemChecked(position, true);
                setTitle(dataList.get(position).getItemName());
                mDrawerLayout.closeDrawer(mDrawerList);
            }
        }
    
    }
    

    and the fragment:

    public class ContactFormFragment extends Fragment {
    
        public ContactFormFragment() {
    
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            ca = getActivity();
            Reachability.registerReachability(ca.getApplicationContext());
    
            settings = ca.getSharedPreferences(Konstanten.PREFERENCES_FILE, 0);
            member = new Gson().fromJson(settings.getString(Konstanten.MEMBER_OBJECT, null), Member.class);
    
            latoFontLight = Tools.getFont(ca.getAssets(), "Lato-Light.ttf");
            latoFontBold = Tools.getFont(ca.getAssets(), "Lato-Bold.ttf");
    
            // Update action bar menu items?
            setHasOptionsMenu(true);
        }
    
        @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
            // Do something that differs the Activity's menu here
            super.onCreateOptionsMenu(menu, inflater);
    
            menu.clear();
    
            if (Build.VERSION.SDK_INT >= 11) {
                // selectMenu(menu);
                inflater.inflate(R.menu.contactform_send_menu, menu);
            }
        }
    }
    

    When debugging I can see that setHasOptionsMenu(true); gets called in onCreate() and I also get into the onCreateOptionsMenu() of ContactFormFragment.java. I just don't understand why the action bar keeps its initial menu items and doesn't replace them. What am I missing?

    Thanks for any help.

    • Wolfram Rittmeyer
      Wolfram Rittmeyer about 10 years
      When debugging does it call onCreateOptionsMenu() in your Activity afterwards (that is after closing the drawer)?
    • Dominik
      Dominik about 10 years
      @WolframRittmeyer yes it does. After the drawer has closed onCreateOptionsMenu() is being called again in the parent Activity. I guess that destroys the menu items from the fragment. How can I avoid this?
    • Leap Hawk
      Leap Hawk over 7 years
      try calling menu.clear() and the inflater.inflate before super.onCreateOptionsMenu()
  • Dominik
    Dominik almost 10 years
    I've tried it with adding menu.clear() to onCreateOptionsMenu() in the Activity. But that didn't have any effect on changing the action bar menu when switching between fragment. Could you explain a bit more in detail how I would have to do this with the Interface you suggested? Thank you.
  • Dominik
    Dominik almost 10 years
    I found the solution and answered by own question. The main problem was that I had a drop down menu in one fragment that didn't disappear when switching the fragment. So I had to remove that manually each time I change from fragment 0 to fragment n.
  • Sam
    Sam almost 10 years
    Firstly, rather than implementing an interface on your fragments, you could just have the activity call setHasOptionsMenu(true/false) directly on the fragment depending on the drawer state. BUT, there is a further problem when using nested fragments - the activity would have to reach down into its fragment tree and hunt all fragments to turn the menus off. Therefore it's preferable to flip the paradigm and have all the fragments use getActivity() and implement an interface on the activity to reveal the drawer open/close state.