ChatGPT解决这个技术问题 Extra ChatGPT

How to Animate Addition or Removal of Android ListView Rows

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!

did someone find the answer to this? pls shareee
@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, 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.

B
Blundell
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>

It's better to use anim.setAnimationListener(new AnimationListener() { ... }) instead of using a Handler
what is populateList() for?
@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.
"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.
What is FavouritesManager class and populateList() method ??
E
Eric

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

https://i.stack.imgur.com/Q7oYK.gif

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

commit of the classic Hello World Android app commit, adding a RecyclerView to the project (content not dynamic) commit, adding functionality to modify content of RecyclerView at runtime (but no animations) and finally...commit adding animations to the RecyclerView


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.
The final step is the missing link I had. stable Id's. Thanks!
This is a great sample project that demonstrates the API well.
d
dimetil

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.


w
whlk

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?


It doesn't make sense to reimplement the listview just to add animations.
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 :(
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.
S
Sam Dufel

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.


k
karabara

call listView.scheduleLayoutAnimation(); before changing the list


C
Community

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!


R
Rafael

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

ListView.smoothScrollToPosition(position);

Y
Yevgeny Simkin

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?


Hey thanks, I'll look into that. Unfortunately it looks like animateLayoutChanges is new as of API Level 11 aka Honeycomb aka can't-use-this-on-any-phones-yet :(
S
Sparkup

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.


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.
R
RevanthKrishnaKumar V.

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.


R
RevanthKrishnaKumar V.

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.


D
Dori

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!)


M
Mina Wissa

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;
}