在工具栏溢出菜单中显示菜单项图标时,这种奇怪的情况是如何发生的?

Posted

技术标签:

【中文标题】在工具栏溢出菜单中显示菜单项图标时,这种奇怪的情况是如何发生的?【英文标题】:How does this strange condition happens when show menu item icon in toolbar overflow menu? 【发布时间】:2015-07-16 13:40:07 【问题描述】:

我想在工具栏中显示一个溢出菜单(AppCompat-v7:22.1.1),下面是我的 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" />

运行我的应用后,菜单项的图标不显示,然后我尝试了这个solution,在我的Activty中添加一个覆盖方法onMenuOpened()(扩展自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);

但是运行这个demo后,发现图标还是没有显示出来。

从这个reported issue,我知道AppCompatActivity.onMenuOpened在22.x中不再被调用,但奇怪的是当我点击Genymotion中的硬件菜单键时,菜单出现在底部并带有图标,

关闭菜单后,我再次点击工具栏中的溢出按钮,菜单中出现这些图标,

多么奇怪啊!为什么会这样?

【问题讨论】:

【参考方案1】:

对于 AppCompactActivity,您可以将此检查放在 onPrepareOptionsPanel() 上。

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

【讨论】:

这可能是一个品味问题,但我认为if (menu.getClass().equals(MenuBuilder.class))if (menu.getClass().getSimpleName().equals("MenuBuilder")) 更优雅。恕我直言,硬编码的字符串越少越好 ;-) 忽略我的评论。按照自己的方式进行操作使其适用于 Android 的本机 MenuBuilder 和 AppCompat。干得好! 如果你使用这个和proguard,你需要排除适当的类:-keep class android.support.v7.view.menu.MenuBuilder *; -keep class com.android.view.menu.MenuBuilder *; 不扩展 appcompatactivity 的片段和活动怎么办?【参考方案2】:

这是对 Alécio Carvalho 上面提供的出色答案的修改。如果需要正确显示图标而不是在主应用程序的操作栏中,而是在每个单独片段内的自定义工具栏中正确显示图标,则此修改适用(我想要一个具有自己标题和自定义操作的单独工具栏每个片段的菜单,而不是简单地将新项目添加到整个 AppCompatActivity 的操作栏)。

对于上述情况,Fragment 类如下:

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;
   

然后my_fragment_layout.xml包含菜单如下

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

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

//...other items

</LinearLayout>

一个典型的菜单文件被实现为res/menu/my_fragment_menu.xml。 片段被添加到 mainActivity 的布局中,就像

<fragment android:id="@+id/my_fragment_id"
android:name="com.appspot.trendy.trendychart.MyFragment"
android:layout_
android:layout_/>

【讨论】:

Bravo! 这个答案还说明了如何在具有setHasOptionsMenu(false);Fragment 内部部署ToolBar 的更普遍的问题。 IE:如何保持ActivityFragment 菜单系统完全独立。【参考方案3】:
@SuppressLint("RestrictedApi")
public void initToolBar()    
    MenuBuilder menuBuilder = (MenuBuilder) toolbar.getMenu();
    menuBuilder.setOptionalIconsVisible(true);

我是这样解决的。

【讨论】:

您如何处理交替的白/黑颜色?使用 always/never 代替 ifRoom 来强制哪些去哪里?【参考方案4】:

这对我有用

  @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;
    

【讨论】:

以上是关于在工具栏溢出菜单中显示菜单项图标时,这种奇怪的情况是如何发生的?的主要内容,如果未能解决你的问题,请参考以下文章

如何更改工具栏导航和溢出菜单图标(appcompat v7)?

如何在 ActionBar 的溢出菜单中显示图标

在reactjs中显示菜单项后无法显示图标。

CardView 内的工具栏,用于创建弹出菜单(溢出图标)

操作栏菜单项图标在RTL时无法正确显示

我的 jQuery 和 CSS 点击切换菜单表现得很奇怪