11

I want to show a overflow menu in toolbar(AppCompat-v7:22.1.1), below is my menu_main.xml.

<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<item
    android:id="@+id/action_search"
    android:title="@string/action_search"
    android:icon="@mipmap/ic_menu_search"
    android:orderInCategory="100"
    android:actionViewClass="android.widget.SearchView"
    app:showAsAction="ifRoom"/>

<item
    android:id="@+id/menu_group_chat"
    android:title="@string/menu_group_chat"
    android:icon="@mipmap/ic_menu_groupchat" />

<item
    android:id="@+id/menu_add_friend"
    android:title="@string/menu_add_friend"
    android:icon="@mipmap/ic_menu_add_friend" />

After running my app, the icon of menu item is not displayed, then I tried this solution, add an override method onMenuOpened() in my Activty(extends from AppCompatActivity),

@Override
public boolean onMenuOpened(int featureId, Menu menu) {
    if(menu!=null){
        if(menu.getClass().getSimpleName().equals("MenuBuilder")){
            try {
                Method m = menu.getClass().getDeclaredMethod(
                        "setOptionalIconsVisible", Boolean.TYPE);
                m.setAccessible(true);
                m.invoke(menu, true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    return super.onMenuOpened(featureId, menu);
}

But after running this demo, I find that the icon is still not displayed.

1. Menu with overflow button click

From this reported issue, I know that AppCompatActivity.onMenuOpened is not called any more in 22.x, but it's odd that when I click the hardware menu key in Genymotion, the menu appear at the bottom and with the icon,

2. Menu with menu key click

after closing the menu, I click the overflow button in the toolbar again, these icons in menu appear,

3. Menu with overflow button click

how strange it is! Why this happens?

shaedrich
  • 5,457
  • 3
  • 26
  • 42
wqycsu
  • 290
  • 2
  • 16

4 Answers4

31

For the AppCompactActivity you can put this check on the onPrepareOptionsPanel() instead.

@Override
protected boolean onPrepareOptionsPanel(View view, Menu menu) {
        if (menu != null) {
            if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
                try {
                    Method m = menu.getClass().getDeclaredMethod(
                            "setOptionalIconsVisible", Boolean.TYPE);
                    m.setAccessible(true);
                    m.invoke(menu, true);
                } catch (Exception e) {
                    Log.e(getClass().getSimpleName(), "onMenuOpened...unable to set icons for overflow menu", e);
                }
            }
        }
    return super.onPrepareOptionsPanel(view, menu);
}
Alécio Carvalho
  • 13,481
  • 5
  • 68
  • 74
  • It may be a matter of taste but I think `if (menu.getClass().equals(MenuBuilder.class))` is more elegant than `if (menu.getClass().getSimpleName().equals("MenuBuilder"))`. The less hardcoded strings the better IMHO ;-) – Matthias Jul 07 '15 at 15:21
  • Ignore my comment. Doing it your way makes it works for both Android's native MenuBuilder and AppCompat's. Nice job! – Matthias Jul 07 '15 at 16:15
  • 1
    if you use this and proguard, you'll need to exclude appropriate classes: `-keep class android.support.v7.view.menu.MenuBuilder {*;} -keep class com.android.view.menu.MenuBuilder {*;}` – farkerhaiku Aug 01 '18 at 14:34
  • What about fragments and activities that don't extend appcompatactivity? – MMG Aug 19 '20 at 11:45
  • I had to switch to the method public boolean onCreateOptionsMenu(Menu menu) the rest stayed the same. – Squareoot Nov 12 '22 at 13:58
3

this is works for me

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

        //this section is the one that allows to visualize the icon
        if(menu instanceof MenuBuilder){
            MenuBuilder m = (MenuBuilder) menu;
            //noinspection RestrictedApi
            m.setOptionalIconsVisible(true);
        }

        return true;
    }
Charles-Qx
  • 41
  • 2
1

Here is a modification of the excellent answer provided above by Alécio Carvalho. This modification is for the case if it is necessary to correctly show icons not in the main app's action bar, but in custom toolbars inside each separate fragment (I wanted a separate toolbar with own title and own customized action menu for every fragment, not simply adding new items to the action bar of the overall AppCompatActivity).

For the mentioned case the Fragment class is as follows:

public class MyFragment extends android.support.v4.app.Fragment {
...
   public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        //at first get the very toolbar
        fragmentToolbar = (Toolbar) view.findViewById(R.id.fragment_toolbar);
        fragmentToolbar.setTitle(R.string.title_string);
        fragmentToolbar.showOverflowMenu();

        //now ready to  get the menu's method, which is responsible for icons, and change its properties 
        Menu menu=fragmentToolbar.getMenu();
        Method menuMethod = null;
        try {
           menuMethod = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
           menuMethod.setAccessible(true);
           menuMethod.invoke(menu, true);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        //now all the other stuff necessary for the toolbar operation
        fragmentToolbar.inflateMenu(R.menu.my_fragment_menu);
        fragmentToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem arg0) {
                if(arg0.getItemId() == R.id.menu_item_1){
                   ...
                }
                return false;
            }
        });

        //and now the main stuff of onCreateView
        View view = inflater.inflate(R.layout.my_fragment_layout, container, false);
        return view;
   }
}

Then my_fragment_layout.xml included menu as follows

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar    xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:id="@+id/fragment_toolbar"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        android:elevation="4dp">
    </android.support.v7.widget.Toolbar>

//...other items

</LinearLayout>

A typical menu file was implemented as res/menu/my_fragment_menu.xml. The fragment was added in the mainActivity's layout simply as

<fragment android:id="@+id/my_fragment_id"
android:name="com.appspot.trendy.trendychart.MyFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Elia12345
  • 326
  • 3
  • 5
  • **Bravo!** This answer also illustrates the more general issue of how to deploy a `ToolBar` inside of a `Fragment` having `setHasOptionsMenu(false);`. **IE:** How to keep the `Activity` and `Fragment` menu systems entirely discrete. – Bad Loser Dec 11 '20 at 10:45
1
@SuppressLint("RestrictedApi")
public void initToolBar(){    
    MenuBuilder menuBuilder = (MenuBuilder) toolbar.getMenu();
    menuBuilder.setOptionalIconsVisible(true);
}

I solved it this way.

enter image description here

Zero
  • 2,764
  • 1
  • 17
  • 20
  • What do you do for the alternating white/black colors? Use always/never instead of ifRoom to force which ones go where? – Andrew67 Sep 23 '18 at 06:19