Animate the removal of a ListView item

2019-03-08 04:49发布

问题:

I'm attempting to animate the removal of a ListView item using this:

    mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, final View view, final int i, long l) {
            view.animate().setDuration(500).x(-view.getWidth()).alpha(0f);
            adapter.remove(tasks.get(i));
            adapter.notifyDataSetChanged();
        }
    });

It does not work. I basically followed the advice of the 4th answer from the top of this post:

How to Animate Addition or Removal of Android ListView Rows

However, there's some funny drawing stuff going on, or recycling, or something because while the animation occurs, the item below the one that slides off screen also gets deleted for some reason. The answer that the question asker eventually marked as correct is unfortunately an RTFM towards the whole of Android's source. I've looked through there, and I can't find the notifications pull-down in JellyBean which I'm trying to emulate.

TIA. John

回答1:

I just found a beautiful solution: https://github.com/paraches/ListViewCellDeleteAnimation

Here is video: http://www.youtube.com/watch?v=bOl5MIti7n0

Many thanks to 'paraches' ;)

Edit

As of Android version 22.0.0 the new android.support.v7.widget.RecyclerView is available as a successor of ListView. Standard add/remove/update animations are available out of the box. The widget animations are also easy to customize (few custom animations).

I strongly recommend switching from ListView to RecyclerView if you need custom item animations.



回答2:

Idea: I start animation when the user selects the row. When the animation is complete i remove the row from the adapter.The following code animates the selected row:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final ListView listView = (ListView) findViewById(R.id.listView1);
    final ArrayList<String> values = new ArrayList<String>();
    values.add("Android");
    values.add("iPhone");
    values.add("WindowsMobile");
    values.add("Blackberry");
    values.add("Windows7");

    final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, android.R.id.text1, values);
    listView.setAdapter(adapter);

    final Animation animation = AnimationUtils.loadAnimation(this,
            R.anim.slide_out);
    animation.setAnimationListener(new AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            adapter.remove(adapter.getItem(selectedRow));
            adapter.notifyDataSetChanged();
        }
    });

    listView.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {
            Toast.makeText(getApplicationContext(),
                    " " + values.get(position), Toast.LENGTH_LONG).show();
            view.startAnimation(animation);
            selectedRow = position;

        }

    });
}

slide_out.xml file:

<?xml version="1.0" encoding="utf-8"?>
 <set xmlns:android="http://schemas.android.com/apk/res/android" >  
    <translate 
        android:fromYDelta="0%" 
        android:toYDelta="100%"  
        android:duration="@android:integer/config_mediumAnimTime"/>  
</set> 


回答3:

As Chet Haase points out in DevBytes, https://www.youtube.com/watch?v=8MIfSxgsHIs, all of this will work but don't forget to add transientState flags.

From his code:

// Here's where the problem starts - this animation will animate a View object.
                    // But that View may get recycled if it is animated out of the container,
                    // and the animation will continue to fade a view that now contains unrelated
                    // content.
                    ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, 0);
                    anim.setDuration(1000);
                    if (setTransientStateCB.isChecked()) {
                        // Here's the correct way to do this: if you tell a view that it has
                        // transientState, then ListView ill avoid recycling it until the
                        // transientState flag is reset.
                        // A different approach is to use ViewPropertyAnimator, which sets the
                        // transientState flag internally.
                        view.setHasTransientState(true);
                    }
                    anim.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            cheeseList.remove(item);
                            adapter.notifyDataSetChanged();
                            view.setAlpha(1);
                            if (setTransientStateCB.isChecked()) {
                                view.setHasTransientState(false);
                            }
                        }
                    });
                    anim.start();


回答4:

I have used the Top to bottom slide animation on refreshing the ListView. Here is the code I used:

 adapter.notifyDataSetChanged();
 Animation animation = AnimationUtils.loadAnimation(
                    getApplication(), R.anim.slide_top_to_bottom);
 getListView().startAnimation(animation);
 getListView().setSelection(0);

and slide_top_to_bottom.xml (save it inside res/anim folder)

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android"   android:interpolator="@android:anim/accelerate_interpolator">
<translate android:fromYDelta="-100%" android:toXDelta="0" android:duration="100" />
<alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="50" />
</set>

EDIT: Try this out:

mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, final View view, final int i, long l) {

    ValueAnimator fader = ObjectAnimator.ofFloat(view, "alpha", 1, 0);
    AnimatorSet animation = new AnimatorSet();
    animation.addListener(new AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {

        }
        @Override
        public void onAnimationRepeat(Animator animation) {
        }
        @Override
        public void onAnimationEnd(Animator animation) {

        }
        @Override
        public void onAnimationCancel(Animator animation) {
        }
    });
    ((AnimatorSet) animation).play(fader);
    animation.setDuration(500);
    animation.start();
    adapter.remove(tasks.get(i));
    adapter.notifyDataSetChanged();
}
});