Question

I have an app using the ActionBar, where I handle orientation changes myself:

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

...and the menu should fit in the ActionBar without overflow in landscape, but not in portrait:

<?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>

On startup, this works correctly:

Landscape: wide ActionBar with all items in

Portrait: narrow ActionBar with overflow (yes, I could force all items to always display and they would fit, as shown below, but that might break on a smaller tablet)

When the emulator changes orientation, the ActionBar's capacity doesn't seem to change:

Portrait, when I started in landscape: narrow ActionBar with all items in (this is ok, but inconsistent)

Landscape, when I started in portrait: wide ActionBar with overflow This looks really silly and is the reason I want to fix this.

I added this call to invalidateOptionsMenu(), but it doesn't help:

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

(Actually I call it by reflection for backward compatibility, but the debugger tells me it really is called and does not encounter an exception.)

invalidateOptionsMenu() actually ends up calling onCreateOptionsMenu() (which re-inflates the menu) before returning, and I can see inside the latter that getResources().getConfiguration().orientation has already changed. So this is really puzzling. If the options menu is being recreated, when the orientation has changed, it must be ActionBar itself caching the width?

Is there a way to re-create the ActionBar without destroying/creating the Activity? (because the latter is a bit expensive in my case)

Edit: Here's a minimal sample project showing the issue.

Edit 2: I had thought of checking the screen width and programmatically adjusting the showAsAction flags between always and never appropriately, but that requires knowing (or guessing) the width of each item. ActionBar's public API does not help me on that point.

Was it helpful?

Solution 2

I've cautiously worked around this: when the device's width is greater than 850dip, force showing all items in the ActionBar, otherwise continue to let the platform decide.

Here's the git commit. Edit: and the follow-up commit to fix using a field that's too new, oops. :-)

I'm definitely still interested in better answers (other than waiting for a fix to the platform).

OTHER TIPS

I ran into this problem to and came up with a hack which works 100%.

I eneded up calling invalidateOptionsMenu() two times. Look at the code below:

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

}

And in your onCreateOptionsMenu() set this on you menuitems you want to show:

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

Not a pretty solution but it works.

I found that the simplest and most effective workaround was to clear the menu and then rebuild it.

For example:

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.
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top