Android development blog
Tutorials about Android dev topics

Android ListView animation

by Francesco Azzola, April 22, 2013
Topics covered

How to apply animation to ListView

Animation

AnimationListener

Delete item with animation

We talked about ListView and Custom Adapter, but an aspect we want to consider is how to animate the ListView. In this example we will show how to animate Listview when we delete items using AnimatorListener.
As example we will use an ArrayAdapter that is populated by a large number of items. In this case we will use android.R.layout.simple_list_item_1 as row layout.
We will focus our attention on the item animation. For example we want that selected item will fade until they disappear.
The first thing we need to do is creating our animation. We will an xml file called fade_anim.xml under res/anim.
The xml file is very simple, we define that we want that our animation moves from fade 1 to fade 0 with a linear interpolator.

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
       android:fromAlpha="1.0" android:toAlpha="0" 
       android:interpolator="@android:anim/linear_interpolator"
       android:duration="1000"/>
 
Now in the main activity we simply populate our ListView with an ArrayAdapter like:

@Override
protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     ListView lv = (ListView) findViewById(R.id.listView);
     List<ItemDetail> itemList = createItems(50);
     final ArrayAdapter<ItemDetail> aAdpt = 
           new ArrayAdapter<ItemDetail>(this,                  
                  android.R.layout.simple_list_item_1, itemList);

     lv.setAdapter(aAdpt);
..
}

Where createItems method simply create items like item1,item2,...
The first thing we have to load is our animation in the onCreate with:

// Load animation
final Animation anim = AnimationUtils.loadAnimation(this, R.anim.fade_anim);

Now we want to start our animation when user clicks on an item inside the ListView. But we need first run the animation and when the animation ends we have to remove item from the list. We need, then, an AnimationListener to coordinate these phases (line 6).
So we have first create the listener and then start the animation (line 21) like:

lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, 
                        final View view, final int position,
   long id) {
   anim.setAnimationListener(new Animation.AnimationListener() {               

                         @Override
                  public void onAnimationStart(Animation animation) {
                  }
       
                         @Override
                  public void onAnimationRepeat(Animation animation) {}
     
                 @Override
                 public void onAnimationEnd(Animation animation) {
      ItemDetail item = aAdpt.getItem(position);
      aAdpt.remove(item);
                }
              });
          view.startAnimation(anim);
 }
});

Let's run the app. If we clicks on an item we will see that it works correctly, first starts the fading animation and then the item is removed.
Come back to xml file and make the animation duration longer  (for example 3sec=3000), and run it again. While we click on an item and it starts fading let's scroll the list.
What is happening?
Well we can notice that other items are fading even if we didn't click them at all!! Why??
This is because OS re-use the view with other items. So we need to find a way to tell the to OS that while the item is fading the corresponding view shouldn't be used.
How can we do it?!
Well in the onAnimationStart (line 10), when the animation is starting, we can flag the view with transient state, that tells to the OS to not re-use the view.
When the animation ends (line 20), we tell to the OS that the view can be re-used and set the transient to the default value while we set the fade value to 1.

lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, 
                        final View view, final int position,
   long id) {
   anim.setAnimationListener(new Animation.AnimationListener() {               

                         @Override
                  public void onAnimationStart(Animation animation) {
                            view.setHasTransientState(true);
                  }
       
                         @Override
                  public void onAnimationRepeat(Animation animation) {}
     
                 @Override
                 public void onAnimationEnd(Animation animation) {
                            ItemDetail item = aAdpt.getItem(position);
                            aAdpt.remove(item);
                            view.setHasTransientState(false);
                }
              });
          view.startAnimation(anim);
 }
});

Now everything works correctly even if we scroll the ListView while the item is fading.

Source  @ github

1 comment:

  1. survivingwithandroidFebruary 20, 2014 at 2:36 PM

    The method setHasTransientState works from API 16, so it won't work on API 15. I'm sorry. Look here http://developer.android.com/reference/android/view/View.html#setHasTransientState(boolean)

    ReplyDelete

Related Posts with Thumbnails