217

In iOS, there is a very easy and powerful facility to animate the addition and removal of UITableView rows, here's a clip from a youtube video showing the default animation. Note how the surrounding rows collapse onto the deleted row. This animation helps users keep track of what changed in a list and where in the list they were looking at when the data changed.

Since I've been developing on Android I've found no equivalent facility to animate individual rows in a TableView. Calling notifyDataSetChanged() on my Adapter causes the ListView to immediately update its content with new information. I'd like to show a simple animation of a new row pushing in or sliding out when the data changes, but I can't find any documented way to do this. It looks like LayoutAnimationController might hold a key to getting this to work, but when I set a LayoutAnimationController on my ListView (similar to ApiDemo's LayoutAnimation2) and remove elements from my adapter after the list has displayed, the elements disappear immediately instead of getting animated out.

I've also tried things like the following to animate an individual item when it is removed:

@Override
protected void onListItemClick(ListView l, View v, final int position, long id) {
    Animation animation = new ScaleAnimation(1, 1, 1, 0);
    animation.setDuration(100);
    getListView().getChildAt(position).startAnimation(animation);
    l.postDelayed(new Runnable() {
        public void run() {
            mStringList.remove(position);
            mAdapter.notifyDataSetChanged();
        }
    }, 100);
}

However, the rows surrounding the animated row don't move position until they jump to their new positions when notifyDataSetChanged() is called. It appears ListView doesn't update its layout once its elements have been placed.

While writing my own implementation/fork of ListView has crossed my mind, this seems like something that shouldn't be so difficult.

Thanks!

Alex Pretzlav
  • 15,505
  • 9
  • 57
  • 55
  • did someone find the answer to this? pls shareee – AndroidGecko Sep 12 '12 at 13:14
  • @Alex: have you done that animation like that youtube video (like iphone), If you have any demo or link for android then please give me, I have tried using animateLayoutChanges in xml file, but it is not exactly like iphone – Jayesh Nov 17 '14 at 13:57
  • @Jayesh, unfortunately I no longer work on Android development. I haven't tested any of the answers to this question that were written after around 2012. – Alex Pretzlav Nov 18 '14 at 16:44

14 Answers14

127
Animation anim = AnimationUtils.loadAnimation(
                     GoTransitApp.this, android.R.anim.slide_out_right
                 );
anim.setDuration(500);
listView.getChildAt(index).startAnimation(anim );

new Handler().postDelayed(new Runnable() {

    public void run() {

        FavouritesManager.getInstance().remove(
            FavouritesManager.getInstance().getTripManagerAtIndex(index)
        );
        populateList();
        adapter.notifyDataSetChanged();

    }

}, anim.getDuration());

for top-to-down animation use :

<set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate android:fromYDelta="20%p" android:toYDelta="-20"
            android:duration="@android:integer/config_mediumAnimTime"/>
        <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
            android:duration="@android:integer/config_mediumAnimTime" />
</set>
Blundell
  • 75,855
  • 30
  • 208
  • 233
OAK
  • 1,486
  • 2
  • 10
  • 6
  • 71
    It's better to use `anim.setAnimationListener(new AnimationListener() { ... })` instead of using a Handler – Oleg Vaskevich Dec 07 '12 at 22:50
  • 1
    what is `populateList()` for? – Vasily Sochinsky May 19 '13 at 07:55
  • @OlegVaskevich and why is that? – newton_guima Oct 21 '13 at 20:50
  • 5
    @MrAppleBR for several reasons. First, you're not creating an unneeded `Handler`. Second, it makes the code less complex by already using a built-in callback for when the animation finishes. Third, you don't know how the implementation of `Handler` and `Animation` will work on each platform; it may be possible that your delayed runnable happens before the animation finishes. – Oleg Vaskevich Oct 21 '13 at 23:41
  • @OlegVaskevich allright.. i really didn't know why it was better.. well explained! – newton_guima Oct 22 '13 at 03:35
  • 8
    "However, the rows surrounding the animated row don't move position until they jump to their new positions when notifyDataSetChanged() is called." this problem still persists in your solution. – Boris Rusev Feb 27 '14 at 22:40
  • 5
    What is FavouritesManager class and populateList() method ?? – Jayesh Nov 17 '14 at 13:45
  • 1
    The delete animation is not working for me. If the ListView is scrolled to index 0, it works fine. However, if I scroll to some arbitrary part of the list and delete an item, the animation will happen on a different list item. – Andrew Apr 25 '15 at 02:54
  • While this animates the view, the height of the view remains the same until the animation ends. A more professional solution would reduce the height of the row during animation. A more convoluted solution that does that can be found at: https://www.youtube.com/watch?v=CPxkoe2MraA&index=75&list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0 – Johann Jul 05 '15 at 21:14
  • @Andrew I had to get the position of the element deleted and subtract from listView.getFirstVisiblePosition() to get the visible index. works like a charm past that! – aherrick Aug 14 '15 at 18:02
  • Can anyone please tell whether to add this code in onCreate()? – Parthiban M Feb 02 '16 at 15:57
16

The RecyclerView takes care of adding, removing, and re-ordering animations!

RecyclerView in action

This simple AndroidStudio project features a RecyclerView. take a look at the commits:

  1. commit of the classic Hello World Android app
  2. commit, adding a RecyclerView to the project (content not dynamic)
  3. commit, adding functionality to modify content of RecyclerView at runtime (but no animations)
  4. and finally...commit adding animations to the RecyclerView
Eric
  • 16,397
  • 8
  • 68
  • 76
  • 1
    I think this is the simple way when you need a simple animation, but you can also custom the animation using the RecyclerView.setItemAnimator() method. – qatz Mar 04 '16 at 01:55
  • 2
    The final step is the missing link I had. stable Id's. Thanks! – Amir Uval Nov 29 '16 at 15:49
  • This is a great sample project that demonstrates the API well. – Mr-IDE Apr 18 '17 at 12:33
10

Take a look at the Google solution. Here is a deletion method only.

ListViewRemovalAnimation project code and Video demonstration

It needs Android 4.1+ (API 16). But we have 2014 outside.

dimetil
  • 3,851
  • 2
  • 29
  • 47
3

call listView.scheduleLayoutAnimation(); before changing the list

karabara
  • 547
  • 6
  • 24
3

After inserting new row to ListView, I just scroll the ListView to new position.

ListView.smoothScrollToPosition(position);
Rafael
  • 6,091
  • 5
  • 54
  • 79
3

Since ListViews are highly optimized i think this is not possible to accieve. Have you tried to create your "ListView" by code (ie by inflating your rows from xml and appending them to a LinearLayout) and animate them?

whlk
  • 15,487
  • 13
  • 66
  • 96
  • 6
    It doesn't make sense to reimplement the listview just to add animations. – Macarse Oct 17 '10 at 02:10
  • I certainly could just use a `LinearLayout`, however with very large datasets `LinearLayout`'s performance will become abysmal. `ListView` has lots of smart optimizations around view recycling that allow it to handle large data sets (iOS's UITableView has the same optimizations). Writing my own view recycler falls under the category of reimplementing ListView :( – Alex Pretzlav Oct 17 '10 at 02:15
  • I know it doesnt make sense - but if you work with just some few rows the benefit of having nice in/out animations could be worth the try. – whlk Oct 17 '10 at 21:49
3

Have you considered animating a sweep to the right? You could do something like drawing a progressively larger white bar across the top of the list item, then removing it from the list. The other cells would still jerk into place, but it'd better than nothing.

Sam Dufel
  • 17,560
  • 3
  • 48
  • 51
3

I hacked together another way to do it without having to manipulate list view. Unfortunately, regular Android Animations seem to manipulate the contents of the row, but are ineffectual at actually shrinking the view. So, first consider this handler:

private Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
    Bundle bundle = message.getData();

    View view = listView.getChildAt(bundle.getInt("viewPosition") - 
        listView.getFirstVisiblePosition());

    int heightToSet;
    if(!bundle.containsKey("viewHeight")) {
        Rect rect = new Rect();
        view.getDrawingRect(rect);
        heightToSet = rect.height() - 1;
    } else {
        heightToSet = bundle.getInt("viewHeight");
    }

    setViewHeight(view, heightToSet);

    if(heightToSet == 1)
        return;

    Message nextMessage = obtainMessage();
    bundle.putInt("viewHeight", (heightToSet - 5 > 0) ? heightToSet - 5 : 1);
    nextMessage.setData(bundle);
    sendMessage(nextMessage);
}

Add this collection to your List adapter:

private Collection<Integer> disabledViews = new ArrayList<Integer>();

and add

public boolean isEnabled(int position) {
   return !disabledViews.contains(position);
}

Next, wherever it is that you want to hide a row, add this:

Message message = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putInt("viewPosition", listView.getPositionForView(view));
message.setData(bundle);
handler.sendMessage(message);    
disabledViews.add(listView.getPositionForView(view));

That's it! You can change the speed of the animation by altering the number of pixels that it shrinks the height at once. Not real sophisticated, but it works!

Community
  • 1
  • 1
Jonathan Schneider
  • 26,852
  • 13
  • 75
  • 99
2

As i had explained my approach in my site i shared the link.Anyways the idea is create bitmaps by getdrawingcache .have two bitmap and animate the lower bitmap to create the moving effect

Please see the following code:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
    {
        public void onItemClick(AdapterView<?> parent, View rowView, int positon, long id)
        {
            listView.setDrawingCacheEnabled(true);
            //listView.buildDrawingCache(true);
            bitmap = listView.getDrawingCache();
            myBitmap1 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), rowView.getBottom());
            myBitmap2 = Bitmap.createBitmap(bitmap, 0, rowView.getBottom(), bitmap.getWidth(), bitmap.getHeight() - myBitmap1.getHeight());
            listView.setDrawingCacheEnabled(false);
            imgView1.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap1));
            imgView2.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap2));
            imgView1.setVisibility(View.VISIBLE);
            imgView2.setVisibility(View.VISIBLE);
            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
            lp.setMargins(0, rowView.getBottom(), 0, 0);
            imgView2.setLayoutParams(lp);
            TranslateAnimation transanim = new TranslateAnimation(0, 0, 0, -rowView.getHeight());
            transanim.setDuration(400);
            transanim.setAnimationListener(new Animation.AnimationListener()
            {
                public void onAnimationStart(Animation animation)
                {
                }

                public void onAnimationRepeat(Animation animation)
                {
                }

                public void onAnimationEnd(Animation animation)
                {
                    imgView1.setVisibility(View.GONE);
                    imgView2.setVisibility(View.GONE);
                }
            });
            array.remove(positon);
            adapter.notifyDataSetChanged();
            imgView2.startAnimation(transanim);
        }
    });

For understanding with images see this

Thanks.

RevanthKrishnaKumar V.
  • 1,855
  • 1
  • 21
  • 34
Ram
  • 61
  • 5
2

Here's the source code to let you delete rows and reorder them.

A demo APK file is also available. Deleting rows is done more along the lines of Google's Gmail app that reveals a bottom view after swiping a top view. The bottom view can have an Undo button or whatever you want.

RevanthKrishnaKumar V.
  • 1,855
  • 1
  • 21
  • 34
Johann
  • 27,536
  • 39
  • 165
  • 279
2

Since Android is open source, you don't actually need to reimplement ListView's optimizations. You can grab ListView's code and try to find a way to hack in the animation, you can also open a feature request in android bug tracker (and if you decided to implement it, don't forget to contribute a patch).

FYI, the ListView source code is here.

Sparkup
  • 3,686
  • 2
  • 36
  • 50
Lie Ryan
  • 62,238
  • 13
  • 100
  • 144
  • 1
    This isn't the way that such changes should be implemented. Before anything, you should have the AOSP project building - which is quite a heavy handed approach to adding animation to list views. Please look at implementations in various open source libraries. – OrhanC1 Jun 16 '14 at 16:11
2

I haven't tried it but it looks like animateLayoutChanges should do what you're looking for. I see it in the ImageSwitcher class, I assume it's in the ViewSwitcher class as well?

Yevgeny Simkin
  • 27,946
  • 39
  • 137
  • 236
  • Hey thanks, I'll look into that. Unfortunately it looks like [animateLayoutChanges](http://developer.android.com/reference/android/R.attr.html#animateLayoutChanges) is new as of API Level 11 aka Honeycomb aka can't-use-this-on-any-phones-yet :( – Alex Pretzlav Jun 22 '11 at 16:34
0

I have done something similar to this. One approach is to interpolate over the animation time the height of the view over time inside the rows onMeasure while issuing requestLayout() for the listView. Yes it may be be better to do inside the listView code directly but it was a quick solution (that looked good!)

Dori
  • 18,283
  • 17
  • 74
  • 116
0

Just sharing another approach:

First set the list view's android:animateLayoutChanges to true:

<ListView
        android:id="@+id/items_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:animateLayoutChanges="true"/>

Then I use a handler to add items and update the listview with delay:

Handler mHandler = new Handler();
    //delay in milliseconds
    private int mInitialDelay = 1000;
    private final int DELAY_OFFSET = 1000;


public void addItem(final Integer item) {
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mDataSet.add(item);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mAdapter.notifyDataSetChanged();
                        }
                    });
                }
            }).start();

        }
    }, mInitialDelay);
    mInitialDelay += DELAY_OFFSET;
}
Mina Wissa
  • 10,923
  • 13
  • 90
  • 158