Как показать значки в меню переполнения в ActionBar

Я знаю, что это невозможно с использованием нативного API. Есть ли обходной путь для реализации такого взгляда?

17 ответов

Ранее опубликованный ответ, в общем, в порядке. Но это в основном удаляет поведение по умолчанию меню переполнения. Например, сколько значков может отображаться на экранах разных размеров, а затем они выпадают в меню переполнения, когда их невозможно отобразить. Делая выше, вы удаляете много важных функций.

Лучшим способом было бы указать меню переполнения для непосредственного отображения значков. Вы можете сделать это, добавив следующий код в свою активность.

@Override
public boolean onMenuOpened(int featureId, Menu menu)
{
    if(featureId == Window.FEATURE_ACTION_BAR && 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(NoSuchMethodException e){
                Log.e(TAG, "onMenuOpened", e);
            }
            catch(Exception e){
                throw new RuntimeException(e);
            }
        }
    }
    return super.onMenuOpened(featureId, menu);
}

Попробовал это на основе предыдущих ответов, и он отлично работает, по крайней мере, с более поздними версиями библиотеки поддержки (25.1):

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

    if(menu instanceof MenuBuilder){
        MenuBuilder m = (MenuBuilder) menu;
        //noinspection RestrictedApi
        m.setOptionalIconsVisible(true);
    }

    return true;
}

В вашем меню XML, используйте следующий синтаксис для вложения меню, вы начнете получать меню с иконками

<item
    android:id="@+id/empty"
    android:icon="@drawable/ic_action_overflow"
    android:orderInCategory="101"
    android:showAsAction="always">
    <menu>
        <item
            android:id="@+id/action_show_ir_list"
            android:icon="@drawable/ic_menu_friendslist"
            android:showAsAction="always|withText"
            android:title="List"/>
    </menu>
</item>

Вы можете использовать SpannableString

public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_tab, menu);

    MenuItem item = menu.findItem(R.id.action_login);
    SpannableStringBuilder builder = new SpannableStringBuilder("* Login");
    // replace "*" with icon
    builder.setSpan(new ImageSpan(this, R.drawable.login_icon), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    item.setTitle(builder);
}

Ответ от Саймона был очень полезен для меня, поэтому я хочу поделиться тем, как я реализовал его в onCreateOptionsMenu-метод, как предлагалось:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu items for use in the action bar
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.main_action_bar, menu);

    // To show icons in the actionbar's overflow menu:
    // http://stackru.com/questions/18374183/how-to-show-icons-in-overflow-menu-in-actionbar
    //if(featureId == Window.FEATURE_ACTION_BAR && 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(NoSuchMethodException e){
                Log.e(TAG, "onMenuOpened", e);
            }
            catch(Exception e){
                throw new RuntimeException(e);
            }
        }
    //}

    return super.onCreateOptionsMenu(menu);
}

Основываясь на ответе @Desmond Lua сверху, я создал статический метод для использования в раскрывающемся списке объекта, объявленного в XML, и обеспечения того, чтобы его окрашенный цвет не влиял на состояние Constant Drawable.

    /**
 * Updates a menu item in the dropdown to show it's icon that was declared in XML.
 *
 * @param item
 *         the item to update
 * @param color
 *         the color to tint with
 */
private static void updateMenuWithIcon(@NonNull final MenuItem item, final int color) {
    SpannableStringBuilder builder = new SpannableStringBuilder()
            .append("*") // the * will be replaced with the icon via ImageSpan
            .append("    ") // This extra space acts as padding. Adjust as you wish
            .append(item.getTitle());

    // Retrieve the icon that was declared in XML and assigned during inflation
    if (item.getIcon() != null && item.getIcon().getConstantState() != null) {
        Drawable drawable = item.getIcon().getConstantState().newDrawable();

        // Mutate this drawable so the tint only applies here
        drawable.mutate().setTint(color);

        // Needs bounds, or else it won't show up (doesn't know how big to be)
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        ImageSpan imageSpan = new ImageSpan(drawable);
        builder.setSpan(imageSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        item.setTitle(builder);
    }
}

И использование этого будет выглядеть примерно так при использовании в деятельности. Это может быть еще более элегантно в зависимости от ваших индивидуальных потребностей.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_activity_provider_connect, menu);
    int color = ContextCompat.getColor(this, R.color.accent_dark_grey);
    updateMenuWithIcon(menu.findItem(R.id.email), color);
    updateMenuWithIcon(menu.findItem(R.id.sms), color);
    updateMenuWithIcon(menu.findItem(R.id.call), color);
    return true;
}

Самый простой способ, который я нашел, это:

public boolean onCreateOptionsMenu(Menu menu){
     MenuInflater inflater = getMenuInflater();
     inflater.inflate(R.menu.toolbar_menu,menu);
     if(menu instanceof MenuBuilder) {  //To display icon on overflow menu

          MenuBuilder m = (MenuBuilder) menu; 
          m.setOptionalIconsVisible(true);

     }
   return true;
}        `

Текущее лучшее, но не принятое решение, вероятно, работает на старых платформах. В любом случае, в новом AppCompat21+ требуемый метод не существует, а метод getDeclaredMethod возвращает исключение NoSuchMethodException,

Поэтому обходной путь для меня (проверенный и работающий на устройствах 4.x, 5.x) основан на параметре фона прямого изменения. Так что просто поместите этот код в свой класс Activity.

@Override
public boolean onMenuOpened(int featureId, Menu menu) {
    // enable visible icons in action bar
    if (featureId == Window.FEATURE_ACTION_BAR && menu != null) {
        if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
            try {
                Field field = menu.getClass().
                        getDeclaredField("mOptionalIconsVisible");
                field.setAccessible(true);
                field.setBoolean(menu, true);
            } catch (IllegalAccessException | NoSuchFieldException e) {
                Logger.w(TAG, "onMenuOpened(" + featureId + ", " + menu + ")", e);
            }
        }
    }
    return super.onMenuOpened(featureId, menu);
}

Котлин:

      @SuppressLint("RestrictedApi")
fun Menu.showOptionalIcons() {
    this as MenuBuilder
    setOptionalIconsVisible(true)
}

Ответ @Simon действительно работает хорошо... но вы используете AppCompat Activity... вам нужно будет использовать этот код вместо этого... Потому что onMenuOpened() больше не вызывается в appcompat-v7:22.x

@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(NoSuchMethodException e){
                    Log.e(Constants.DEBUG_LOG, "onMenuOpened", e);
                }
                catch(Exception e){
                    throw new RuntimeException(e);
                }
            }
        }
        return super.onPrepareOptionsPanel(view, menu);
    }

По моему мнению, это возможно только путем создания пользовательской панели инструментов. Потому что ActionBar по умолчанию не дает вам эту функцию. Но вы можете поместить значки, взяв подменю в качестве дочернего элемента. И если у вас есть лучшее решение, чем я.. просто сообщите мне.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
<item
    android:id="@+id/action_settings"
    android:icon="@drawable/ic_menu_camera"
    android:showAsAction="never"
    android:title="@string/action_settings" />

<item
    android:id="@+id/action_1"
    android:icon="@drawable/ic_menu_gallery"
    android:showAsAction="never"
    android:title="Hello" />

<item
    android:id="@+id/action_search"
    android:icon="@android:drawable/ic_search_category_default"
    android:showAsAction="never"
    android:title="action_search">

    <menu>
        <item
            android:id="@+id/version1"
            android:icon="@android:drawable/ic_dialog_alert"
            android:showAsAction="never"
            android:title="Cup cake" />

        <item
            android:id="@+id/version2"
            android:icon="@drawable/ic_menu_camera"
            android:showAsAction="never"
            android:title="Donut" />


        <item
            android:id="@+id/version3"
            android:icon="@drawable/ic_menu_send"
            android:showAsAction="never"
            android:title="Eclair" />

        <item
            android:id="@+id/version4"
            android:icon="@drawable/ic_menu_gallery"
            android:showAsAction="never"
            android:title="Froyo" />
    </menu>
</item>
</menu> 

Мой простой мод для отличного решения Саймона для использования с ActionMode:

 @Override
    public boolean onPrepareActionMode(ActionMode mode, 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(NoSuchMethodException e){
                    Log.e(TAG, "onPrepareActionMode", e);
                }
                catch(Exception e){
                    throw new RuntimeException(e);
                }
            }
        }
        return true;
    }

Это слишком поздно, но кто-то может помочь моей попытке, я получил помощь от @Desmond Lua, который поможет тем, кто использует menu.xml

Мой ответ для динамического создания меню вот мой код:

  int ACTION_MENU_ID =1;
  SpannableStringBuilder builder;
   @Override
  public boolean onCreateOptionsMenu(Menu menu) {

  //this space for icon 
    builder = new SpannableStringBuilder("  " + getString(R.string.your_menu_title));
    builder.setSpan(new ImageSpan(this,  R.drawable.ic_your_menu_icon), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
  //dynamic menu added 
    menu.add(Menu.NONE,ACTION_MENU_ID, Menu.NONE,
             getString(R.string.your_menu_title))
            .setShowAsAction(MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT | MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
  //set icon in overflow menu      
        menu.findItem(ACTION_MENU_ID).setTitle(builder);
}

Добавьте это в стиле:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/action_settings"
        app:showAsAction="always"
        android:icon="@drawable/ic_more_vert_white"
        android:orderInCategory="100"
        android:title="">
        <menu>

            <item
                android:id="@+id/Login"
                android:icon="@drawable/ic_menu_user_icon"
                android:showAsAction="collapseActionView|withText"
                android:title="@string/str_Login" />

            <item
                android:id="@+id/str_WishList"
                android:icon="@drawable/ic_menu_wish_list_icon"
                android:showAsAction="collapseActionView"
                android:title="@string/str_WishList" />

            <item
                android:id="@+id/TrackOrder"
                android:icon="@drawable/ic_menu_my_order_icon"
                android:showAsAction="collapseActionView"
                android:title="@string/str_TrackOrder" />

            <item
                android:id="@+id/Ratetheapp"
                android:icon="@drawable/ic_menu_rate_the_apps"
                android:showAsAction="collapseActionView"
                android:title="@string/str_Ratetheapp" />

            <item
                android:id="@+id/Sharetheapp"
                android:icon="@drawable/ic_menu_shar_the_apps"
                android:showAsAction="collapseActionView"
                android:title="@string/str_Sharetheapp" />

            <item
                android:id="@+id/Contactus"
                android:icon="@drawable/ic_menu_contact"
                android:showAsAction="collapseActionView"
                android:title="@string/str_Contactus" />

            <item
                android:id="@+id/Policies"
                android:icon="@drawable/ic_menu_policy_icon"
                android:showAsAction="collapseActionView"
                android:title="@string/str_Policies" />
        </menu>
    </item>
</menu>
 public void showContextMenuIconVisible(Menu menu){
    if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
        try {
            Field field = menu.getClass().getDeclaredField("mOptionalIconsVisible");
            field.setAccessible(true);
            field.setBoolean(menu, true);
        } catch (Exception ignored) {
            ignored.printStackTrace();
        }
    }
}

Я использовал предложение MashukKhan, но я всегда показывал элемент-держатель как действие и использовал вертикальные точки из векторных ресурсов в качестве значка.

<item
    android:orderInCategory="10"
    android:title=""
    android:icon="@drawable/ic_more_vert"
    app:showAsAction="always" >

    <menu>
        <item
            android:id="@+id/action_tst1"
            ...and so on

   </menu>
</item>

Это создает эффект "переполнения" меню и отображает значки в раскрывающемся списке.
Можно даже поставить перед ним предметы, если они не противоречат его отображению.
Я считаю решение MashukKhan элегантным, и оно вписывается в решение, а не в категорию работы, потому что это просто реализация подменю.

Включение значков в дополнительном меню можно свести к паре строк кода.

Опоздание на день и недостаток доллара, но его доработка дает немного больше (протестировано на Android 5, 10, 11 и 12 — отладочная сборка):

      @Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_X_activity, menu);
    
    // All other menus show icons, why leave the overflow menu out? It should match.
    if(menu instanceof MenuBuilder) {
        ((MenuBuilder) menu).setOptionalIconsVisible(true);
    }
    
    return super.onCreateOptionsMenu(menu);
}
Другие вопросы по тегам