Android: Removing Fade Effect on ActionBar when using setActionView()

Letting me start by explaining the desired effect I was trying to achieve. On the Android Google+ application if you hit refresh there is a spinner animation that replaces the refresh ActionBar item when pressed. I was able to achieve this but setting the pressed MenuItem to a new layout by using “MenuItem.setActionView()”.

However, on ICS if you have a two ore more MenuItems always available in the Android ActionBar (i.e. you set android:showAsAction=”ifRoom” in the mainmenu.xml file below) swapping the the icon on one MenuItem when pressed causes a slight ghost selection of the other.

The MenuItem next to the one pressed immediately shows a highlight as you stop pressing the refresh icon. This ghost selection behavior is produced because the default ICS background selector contains a fade animation. Swapping the icon causes the animation up/fade to transfer to the next non-customized ActionBar MenuItem, the one you didn’t select.

Here is a code example (not complete) on how to swap out the MenuItem with a custom spinner:

private View mRefreshIndeterminateProgressView; // save inflated layout for reference
private MenuItem refreshItem; // reference to actionbar menu item we want to swap

if (mRefreshIndeterminateProgressView == null) {
   LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   mRefreshIndeterminateProgressView = inflater.inflate(R.layout.actionbar_indeterminate_progress, null);
}
refreshItem.setActionView(mRefreshIndeterminateProgressView); // replace actionbar menu item with progress
// doing refreshItem.setActionView(null) removes the animation

Here is what the actionbar_indeterminate_progress.xml looks like:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:gravity="center"> 
<ProgressBar android:layout_width="16dp" 
    android:layout_height="16dp" 
    android:layout_marginLeft="12dp" 
    android:layout_marginRight="12dp" 
    android:layout_gravity="center" 
    style="?android:attr/indeterminateProgressStyle"/> 
</FrameLayout> 

Here is your mainmenu.xml:

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

<item android:id="@+id/detail_menu_refresh" 
    android:title="Refresh" 
    android:icon="@drawable/ic_action_refresh" 
    android:showAsAction="ifRoom"/> 

 <item android:id="@+id/main_menu_share" 
    android:title="Share" 
    android:icon="@drawable/ic_action_share"
    android:showAsAction="ifRoom"/>

 <item android:id="@+id/main_menu_about" 
    android:title="About" 
    android:icon="@drawable/ic_action_about"
    android:showAsAction="never"/>

 </menu>

The solution is to set your own custom selector background for the ActionBar so you can remove the fade. To do this, I grabbed the abs__item_background_holo_dark.xml from (ActionBarSherlock > Library > res > drawable) and the associated graphics to add to my project. You could easily do this without using ActionBarSherlock and just ActionBar and regular themes for your project. I then removed the following line from the nod in the xml which disables the fade:

 android:exitFadeDuration="@android:integer/config_mediumAnimTime" 

Here is modified abs__item_background_holo_dark.xml:

 
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/abs__list_selector_disabled_holo_dark" />
    <item android:state_focused="true"  android:state_enabled="false" android:drawable="@drawable/abs__list_selector_disabled_holo_dark" />
    <item android:state_focused="true"  android:state_pressed="true" android:drawable="@drawable/abs__list_selector_background_transition_holo_dark" />
    <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/abs__list_selector_background_transition_holo_dark" />
    <item android:state_focused="true"  android:drawable="@drawable/abs__list_focused_holo" />
    <item  android:drawable="@android:color/transparent" />
</selector>

Here is how I set it up in my styles.xml file:

 
<style name="Dark" parent="Theme.Sherlock.Light.DarkActionBar">
    <item name="actionBarStyle">@style/Widget.Styled.ActionBar</item>
    <item name="android:actionBarStyle">@style/Widget.Styled.ActionBar</item>
    <item name="actionBarItemBackground">@drawable/abs__item_background_holo_dark</item>
    <item name="android:actionBarItemBackground">@drawable/abs__item_background_holo_dark</item>
</style>

<style name="Widget.Styled.ActionBar" parent="Widget.Sherlock.Light.ActionBar.Solid.Inverse"/>

I got a great tip on how to fix this from Jake Wharton, the creator of ActionBarSherlock. I recommend using his library if you want to have a consistent ActionBar across all versions of your Android applications.

– Mister

5 Comments

  1. I get an error saying that “?android:attr/indeterminateProgressStyle” is not available for API 11. How would you do this for API 10?

Comments are closed.