Animated Icon for ActionItem Animated Icon for ActionItem android android

Animated Icon for ActionItem


You're on the right track. Here is how the GitHub Gaug.es app will be implementing it.

First they define an animation XML:

<rotate xmlns:android="http://schemas.android.com/apk/res/android"    android:fromDegrees="0"    android:toDegrees="360"    android:pivotX="50%"    android:pivotY="50%"    android:duration="1000"    android:interpolator="@android:anim/linear_interpolator" />

Now define a layout for the action view:

<ImageView xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:src="@drawable/ic_action_refresh"    style="@style/Widget.Sherlock.ActionButton" />

All we need to do is enable this view whenever the item is clicked:

 public void refresh() {     /* Attach a rotating ImageView to the refresh item as an ActionView */     LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);     ImageView iv = (ImageView) inflater.inflate(R.layout.refresh_action_view, null);     Animation rotation = AnimationUtils.loadAnimation(getActivity(), R.anim.clockwise_refresh);     rotation.setRepeatCount(Animation.INFINITE);     iv.startAnimation(rotation);     refreshItem.setActionView(iv);     //TODO trigger loading }

When the loading is done, simply stop the animation and clear the view:

public void completeRefresh() {    refreshItem.getActionView().clearAnimation();    refreshItem.setActionView(null);}

And you're done!

Some additional things to do:

  • Cache the action view layout inflation and animation inflation. They are slow so you only want to do them once.
  • Add null checks in completeRefresh()

Here's the pull request on the app: https://github.com/github/gauges-android/pull/13/files


I've worked a bit on solution using ActionBarSherlock, I've came up with this:

res/layout/indeterminate_progress_action.xml

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="48dp"    android:layout_height="wrap_content"    android:gravity="center"    android:paddingRight="12dp" >    <ProgressBar        style="@style/Widget.Sherlock.ProgressBar"        android:layout_width="44dp"        android:layout_height="32dp"        android:layout_gravity="left"        android:layout_marginLeft="12dp"        android:indeterminate="true"        android:indeterminateDrawable="@drawable/rotation_refresh"        android:paddingRight="12dp" /></FrameLayout>

res/layout-v11/indeterminate_progress_action.xml

<?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        style="@style/Widget.Sherlock.ProgressBar"        android:layout_width="32dp"        android:layout_gravity="left"        android:layout_marginRight="12dp"        android:layout_marginLeft="12dp"        android:layout_height="32dp"        android:indeterminateDrawable="@drawable/rotation_refresh"        android:indeterminate="true" /></FrameLayout>

res/drawable/rotation_refresh.xml

<?xml version="1.0" encoding="utf-8"?><rotate xmlns:android="http://schemas.android.com/apk/res/android"    android:pivotX="50%"    android:pivotY="50%"    android:drawable="@drawable/ic_menu_navigation_refresh"    android:repeatCount="infinite" ></rotate>

Code in activity (I have it in ActivityWithRefresh parent class)

// Helper methodsprotected MenuItem refreshItem = null;  protected void setRefreshItem(MenuItem item) {    refreshItem = item;}protected void stopRefresh() {    if (refreshItem != null) {        refreshItem.setActionView(null);    }}protected void runRefresh() {    if (refreshItem != null) {        refreshItem.setActionView(R.layout.indeterminate_progress_action);    }}

in activity creating menu items

private static final int MENU_REFRESH = 1;@Overridepublic boolean onCreateOptionsMenu(Menu menu) {    menu.add(Menu.NONE, MENU_REFRESH, Menu.NONE, "Refresh data")            .setIcon(R.drawable.ic_menu_navigation_refresh)            .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS);    setRefreshItem(menu.findItem(MENU_REFRESH));    refreshData();    return super.onCreateOptionsMenu(menu);}private void refreshData(){    runRefresh();    // work with your data    // for animation to work properly, make AsyncTask to refresh your data    // or delegate work anyhow to another thread    // If you'll have work at UI thread, animation might not work at all    stopRefresh();}

And the icon, this is drawable-xhdpi/ic_menu_navigation_refresh.png
drawable-xhdpi/ic_menu_navigation_refresh.png

This could be found in http://developer.android.com/design/downloads/index.html#action-bar-icon-pack


In addition to what Jake Wharton said, you should propably do the following to ensure that the animation stops smoothly and does not jump around as soon as the loading finished.

First, create a new boolean (for the whole class):

private boolean isCurrentlyLoading;

Find the method that starts your loading. Set your boolean to true when the activity starts loading.

isCurrentlyLoading = true;

Find the method that is started when your loading is finished. Instead of clearing the animation, set your boolean to false.

isCurrentlyLoading = false;

Set an AnimationListener on your animation:

animationRotate.setAnimationListener(new AnimationListener() {

Then, each time the animation was executed one time, that means when your icon made one rotation, check the loading state, and if not loading anymore, the animation will stop.

@Overridepublic void onAnimationRepeat(Animation animation) {    if(!isCurrentlyLoading) {        refreshItem.getActionView().clearAnimation();        refreshItem.setActionView(null);    }}

This way, the animation can only be stopped if it already rotated till the end and will be repeated shortly AND it is not loading anymore.

This is at least what I did when I wanted to implement Jake's idea.