The problem:
(1) add a touch listener to rows in a listview so that on swipe.
(2) swipe animation plays
(3) rows get deleted in the backend, and
(4) the animation plays without any flicker or jerkiness. By "flicker" I mean that the deleted row briefly shows after the animation finished.
I suspect that something funky was happening with the animation listener, so I ended up doing the following (done in the order given):
- Do the animation and make it persist by setting setFillAfter and setFillenabled to true
- Make the view invisible when the animation ends
- Delete the row in the database
- Reset the animation
- Reload the listview
- Make the view visible (but wait an additional 300 ms)
The result deletes the row without jerkiness or flicker BUT it now feels sluggish because of extra 300 ms wait. (I'm also not sure if this delay works across all devices.)
Update: I should point out that the 300 ms delay is what makes it work. That's weird because by that point the animation was reset and the listview has the latest data. There should be no reason why making the view visible makes the old row briefly show, right?
I also tried using a ViewPropertyAnimator (as per Using animation on a ViewPager and setFillAfter) but for some reason the onAnimationEnd listener was called at every step of the animation.
I also read that we should implement a custom view and override its onAnimationEnd listener. (However, I haven't tried that approach yet.)
Update: just tried to add an extra dummy animation at the end (as per Android Animation Flicker). However, that doesn't work
My test phone runs Ice Cream Sandwich. My app is targeting Gingerbread and after.
So what's the proper solution? Am I doing this the wrong way?
Here's the code:
@Override
public boolean onTouch(final View view, MotionEvent event)
{
//...
switch(action) {
//...
case MotionEvent.ACTION_MOVE:
// ...
if (//check for fling
{
view.clearAnimation();
//animation = standard translate animation
animation.setAnimationListener(new AnimationListener() {
//
@Override
public void onAnimationEnd(Animation animation)
{
view.setVisibility(View.INVISIBLE);
flingListener.onFling(cursorPosition, view, velocity);
}
//...
});
view.startAnimation(animation);
}
break;
//
}
The "fling listener":
@Override
public void onFling(int position, final View view, float velocity)
{
//delete row -- its actually a Loader
//the following code runs in the Loader's onLoadFinished
view.clearAnimation();
adapter.notifyDataSetChanged();
adapter.swapCursor(null);
//reload listview -- it's actually a Loader
//the following code runs in the Loader's onLoadFinished
adapter.swapCursor(cursor);
view.postDelayed(new Runnable() {
@Override
public void run()
{
view.setVisibility(View.VISIBLE);
}
}, 300);
}
Update: After comparing Chet Haase's code, we are doing similar things with some important differences: (1) he uses a onPreDraw listener with the ListView tree observer to do the actual deletion, (2) he removes the row not only from the array but also from the listview. After mimicking his code, it still didn't work. The problem is now the Loader---I use a Loader to delete rows asynchronously. The Loader seems to force an additional draw call to the ListView...before the row has been deleted in the backend. And that's (another) cause of the flicker. Still haven't figured out a workaround though.