Как заставить использование переполнения меню на устройствах с помощью кнопки меню

Я бы хотел, чтобы все пункты меню, которые не вписывались в панель ActionBar, попадали в меню переполнения (то, которое вызывается из панели действий, а не кнопка меню) даже на устройствах, на которых есть кнопка меню. Это кажется гораздо более интуитивно понятным для пользователей, чем выбрасывание их в отдельный список меню, требующий от пользователя перехода от сенсорного (экранного) взаимодействия к взаимодействию на основе кнопок просто потому, что макет панели действий не помещается на панели.

На эмуляторе я могу установить значение "Аппаратная часть / Домашние ключи" на "нет" и получить этот эффект. Я искал способ сделать это в коде для фактического устройства, которое имеет кнопку меню, но не может точно. Может кто-нибудь мне помочь?

12 ответов

Решение

РЕДАКТИРОВАТЬ: Изменено, чтобы ответить за положение кнопки физического меню.

Это фактически предотвращено дизайном. Согласно разделу "Совместимость" в Руководстве по дизайну Android,

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

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

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

Вы также можете использовать этот небольшой взлом здесь:

try {
    ViewConfiguration config = ViewConfiguration.get(this);
    Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
    if (menuKeyField != null) {
        menuKeyField.setAccessible(true);
        menuKeyField.setBoolean(config, false);
    }
} catch (Exception ignored) {
}

Хорошее место, чтобы положить это было бы onCreate -Метод вашего класса приложения.

Это заставит приложение показать меню переполнения. Кнопка меню по-прежнему будет работать, но откроет меню в правом верхнем углу.

[Править] Так как он появлялся уже несколько раз: этот хак работает только для родного ActionBar, представленного в Android 3.0, а не для ActionBarSherlock. Последний использует свою собственную внутреннюю логику, чтобы решить, показывать ли меню переполнения. Если вы используете ABS, все платформы < 4.0 обрабатываются ABS и, следовательно, подчиняются его логике. Хак все равно будет работать на всех устройствах с Android 4.0 или выше (вы можете смело игнорировать Android 3.x, поскольку на самом деле не существует планшетов с кнопкой меню).

Существует специальная тема ForceOverflow, которая заставит меню в ABS, но, по всей видимости, в будущих версиях оно будет удалено из-за сложностей.

Я использую его для обхода, определяя свое меню следующим образом (также с иконкой ActionBarSherlock, используемой в моем примере):

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/menu_overflow"
        android:icon="@drawable/abs__ic_menu_moreoverflow_normal_holo_light"
        android:orderInCategory="11111"
        android:showAsAction="always">
        <menu>
            <item
                android:id="@+id/menu_overflow_item1"
                android:showAsAction="never"
                android:title="@string/overflow_item1_title"/>
            <item
                android:id="@+id/menu_overflow_item2"
                android:showAsAction="never"
                android:title="@string/overflow_item2_title"/>
        </menu>
    </item>

</menu>

Я признаю, что для этого может потребоваться ручное "управление переполнением" в вашем xml, но я нашел это решение полезным.

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

private Menu mainMenu;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // TODO: init menu here...
    // then:
    mainMenu=menu;
    return true;
}

@Override
public boolean onKeyUp(int keycode, KeyEvent e) {
    switch(keycode) {
        case KeyEvent.KEYCODE_MENU:
            if (mainMenu !=null) {
                mainMenu.performIdentifierAction(R.id.menu_overflow, 0);
            }
    }

    return super.onKeyUp(keycode, e);
}

:-)

Если вы используете панель действий из библиотеки поддержки (android.support.v7.app.ActionBar), используйте следующее:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:yorapp="http://schemas.android.com/apk/res-auto" >

    <item
        android:id="@+id/menu_overflow"
        android:icon="@drawable/icon"
        yourapp:showAsAction="always"
        android:title="">
        <menu>
            <item
                android:id="@+id/item1"
                android:title="item1"/>
            <item
                android:id="@+id/item2"
                android:title="item2"/>
        </menu>
    </item>

</menu>

Этот вид метода не поддерживается системой разработки для разработчиков Android, но я нашел способ его пройти:

Добавьте это в ваш файл меню XML:

<item android:id="@+id/pick_action_provider"
    android:showAsAction="always"
    android:title="More"
    android:icon="@drawable/ic_action_overflow"
    android:actionProviderClass="com.example.AppPickActionProvider" />

Затем создайте класс с именем "AppPickActionProvider" и скопируйте в него следующий код:

    package com.example;

import android.content.Context;
import android.util.Log;
import android.view.ActionProvider;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.SubMenu;
import android.view.View;

public class AppPickActionProvider extends ActionProvider implements
        OnMenuItemClickListener {

    static final int LIST_LENGTH = 3;

    Context mContext;

    public AppPickActionProvider(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    public View onCreateActionView() {
        Log.d(this.getClass().getSimpleName(), "onCreateActionView");

        return null;
    }

    @Override
    public boolean onPerformDefaultAction() {
        Log.d(this.getClass().getSimpleName(), "onPerformDefaultAction");

        return super.onPerformDefaultAction();
    }

    @Override
    public boolean hasSubMenu() {
        Log.d(this.getClass().getSimpleName(), "hasSubMenu");

        return true;
    }

    @Override
    public void onPrepareSubMenu(SubMenu subMenu) {
        Log.d(this.getClass().getSimpleName(), "onPrepareSubMenu");

        subMenu.clear();

        subMenu.add(0, 1, 1, "Item1")
        .setIcon(R.drawable.ic_action_home).setOnMenuItemClickListener(this);

        subMenu.add(0, 2, 1, "Item2")
            .setIcon(R.drawable.ic_action_downloads).setOnMenuItemClickListener(this);
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        switch(item.getItemId())
        {
            case 1:

                // What will happen when the user presses the first menu item ( 'Item1' )

                break;
            case 2:

                // What will happen when the user presses the second menu item ( 'Item2' )

                break;

        }

        return true;
    }
}

Ну, я думаю, что Александр Лукас дал (к сожалению) правильный ответ, поэтому я отмечаю его как "правильный". Альтернативный ответ, который я добавляю здесь, состоит в том, чтобы просто указать новым читателям на этот пост в блоге разработчиков Android как довольно полное обсуждение темы с некоторыми конкретными предложениями о том, как обращаться с вашим кодом при переходе с уровня до 11 к новой панели действий.

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

Я не уверен, что это то, что вы ищете, но я создал подменю в меню ActionBar и установил его значок в соответствии со значком переполненного меню. Хотя на него не будут автоматически отправляться элементы (т. Е. Вы должны выбирать, что всегда видно и что всегда переполняется), мне кажется, что этот подход может вам помочь.

Если вы используете панель инструментов, вы можете показать переполнение на всех версиях и на всех устройствах, я пробовал на некоторых устройствах 2.x, это работает.

В приложении gmail, которое поставляется с предустановленной ICS, кнопка меню отключается, если выбрано несколько элементов. Меню переполнения здесь "принудительно" вызывается с помощью кнопки переполнения вместо кнопки физического меню. Существует сторонняя библиотека под названием ActionBarSherlock, которая позволяет вам "форсировать" меню переполнения. Но это будет работать только на уровне API 14 или ниже (до ICS)

Извините, если эта проблема мертва.

Вот что я сделал, чтобы устранить ошибку. Я пошел на макеты и создал два, содержащие панели инструментов. Один был макетом для SDK версии 8, а другой - для SDK версии 21. В версии 8 я использовал android.support.v7.widget.Toolbar, в то время как я использовал android.widget.Toolbar на макете SDK 21.

Затем я надуваю панель инструментов в своей деятельности. Я проверяю SDK, чтобы увидеть, был ли он 21 или выше. Затем я раздуваю соответствующий макет. Это заставит аппаратную кнопку отобразиться на панели инструментов, которую вы на самом деле создали.

      try {
    ViewConfiguration config = ViewConfiguration.get(this);
    Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
    if (menuKeyField != null) {
        menuKeyField.setAccessible(true);
        menuKeyField.setBoolean(config, false);
    }
} catch (Exception ignored) {
}

при обновлении до Android 14 эта часть больше не используется (поскольку она не принадлежит ни одной поддержке sdk). С такой логикой, как я буду конвертировать? Кто-нибудь это делал, подскажите пожалуйста

Для тех, кто использует новый Toolbar:

private Toolbar mToolbar;

@Override
protected void onCreate(Bundle bundle) {
    super.onCreate(bundle);

    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(mToolbar);

    ...
}


@Override
public boolean onKeyUp(int keycode, KeyEvent e) {
    switch(keycode) {
        case KeyEvent.KEYCODE_MENU:
            mToolbar.showOverflowMenu();
            return true;
        }

    return super.onKeyUp(keycode, e);
}
Другие вопросы по тегам