Емкость/переполнение ActionBar не меняется при изменении ориентации

У меня есть приложение, использующее ActionBar, где я сам обрабатываю изменения ориентации:

android:configChanges="keyboard|keyboardHidden|orientation|screenSize"

... и меню должно помещаться в ActionBar без переполнения в альбомной ориентации, но не в портретной:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/Game"  android:id="@+id/game"  android:icon="@android:drawable/ic_menu_manage"     android:showAsAction="ifRoom|withText"/>
<item android:title="@string/Type"  android:id="@+id/type"  android:icon="@android:drawable/ic_menu_edit"       android:showAsAction="ifRoom|withText"/>
<item android:title="@string/Other" android:id="@+id/other" android:icon="@android:drawable/ic_menu_gallery"    android:showAsAction="ifRoom|withText"/>
<item android:title="@string/Solve" android:id="@+id/solve" android:icon="@android:drawable/ic_menu_directions" android:showAsAction="ifRoom|withText"/>
<item android:title="@string/Help"  android:id="@+id/help"  android:icon="@android:drawable/ic_menu_help"       android:showAsAction="ifRoom"/>
</menu>

При запуске это работает правильно:

Альбомная: широкая панель действий со всеми элементами

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

Когда эмулятор меняет ориентацию, емкость панели действий не меняется:

Портрет, когда я начал в альбомной ориентации: сузить панель действий со всеми элементами(это нормально, но непоследовательно)

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

Я добавил этот вызов в invalidateOptionsMenu(), но он не помогает:

@Override
public void onConfigurationChanged(Configuration newConfig)
{
    maybeMoveSomeViewsAround(newConfig);
    super.onConfigurationChanged(newConfig);
    invalidateOptionsMenu();
}

(На самом деле я вызываю его отражением для обратной совместимости, но отладчик говорит мне, что он действительно вызывается и не сталкивается с исключением.)

invalidateOptionsMenu() на самом деле вызывает onCreateOptionsMenu() (который повторно наполняет меню) перед возвратом, и я вижу внутри последнего, что getResources().getConfiguration().ориентация уже изменилась. Так что это действительно озадачивает. Если меню параметров воссоздается, когда ориентация изменилась, это должен быть сам ActionBar, кэширующий ширину?

Есть ли способ воссоздать ActionBar без уничтожения/создания Activity? (поскольку последнее в моем случае немного дороже)

Изменить: вот минимальный пример проекта, показывающий проблему .

Редактировать 2: я подумал о проверке ширины экрана и программной настройке флагов showAsAction между всегда и никогда надлежащим образом, но для этого нужно знать (или угадывать) ширину каждого элемента. общедоступный API ActionBar не помогает мне в этом вопросе.


person Chris Boyle    schedule 02.10.2011    source источник
comment
Можете ли вы создать и опубликовать полный образец проекта, демонстрирующий проблему? Кроме того, вы пробовали это на оборудовании? Возможно, это проблема конкретного эмулятора.   -  person CommonsWare    schedule 02.10.2011
comment
@CommonsWare Хорошо, посмотрите мое редактирование в конце примера проекта. В настоящее время у меня нет планшетного оборудования, но я собираюсь посетить мероприятие Android Developer Lab, посвященное планшетам, в среду, поэтому я, вероятно, смогу попробовать его там.   -  person Chris Boyle    schedule 02.10.2011
comment
Я могу воспроизвести проблему на XOOM под управлением Android 3.2. Это похоже на ошибку. Я пошел дальше и зарегистрировал проблему: code.google.com. /p/android/issues/detail?id=20493   -  person CommonsWare    schedule 02.10.2011


Ответы (3)


Я столкнулся с этой проблемой и придумал хак, который работает на 100%.

Я дважды вызывал invalidateOptionsMenu(). Посмотрите на код ниже:

private boolean hackActionBarReset = false;

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    //a hack to reset the items in the action bar.
    hackActionBarReset = true;
    invalidateOptionsMenu();
    hackActionBarReset = false;
    invalidateOptionsMenu();

}

И в вашем onCreateOptionsMenu() установите это на элементы меню, которые вы хотите показать:

item.setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT | (hackActionBarReset ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_IF_ROOM));

Не очень красивое решение, но оно работает.

person danielnilsson9    schedule 05.07.2012

Я осторожно работал над этим: когда ширина устройства превышает 850dip, принудительно отображать все элементы в панели действий, в противном случае продолжайте позволять платформе решать.

Вот коммит git. Изменить: и последующую фиксацию для исправления с помощью слишком новое поле, упс. :-)

Я определенно все еще заинтересован в лучших ответах (кроме ожидания исправления для платформы).

person Chris Boyle    schedule 02.10.2011

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

Например:

Menu menu;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    this.menu = menu;
    // Setup code goes here...
}

@Override
public void onOrientationChanged() {
    if (menu != null) {
        menu.close(); // Ensure the menu is closed before changing its contents.
        menu.clear();
        onCreateOptionsMenu(menu); // Rebuild the menu.
    }
}
person vaughandroid    schedule 08.01.2013
comment
Вам не нужно делать это так. Просто используйте invalidateOptionsMenu() - person user924; 28.05.2019