Android TranslateAnimation resets after animation

Solution 1:

Here is the actual bug related to this issue

This basically states that the onAnimationEnd(...) method doesn't really work well when an AnimationListener is attached to an Animation

The workaround is to listen for the animation events in the view to which you were applying the animation to For example if initially you were attaching the animation listener to the animation like this

mAnimation.setAnimationListener(new AnimationListener() {
    @Override
    public void onAnimationEnd(Animation arg0) {
                       //Functionality here
    }

and then applying to the animation to a ImageView like this

mImageView.startAnimation(mAnimation);

To work around this issue, you must now create a custom ImageView

public Class myImageView extends ImageView {

and then override the onAnimationEnd method of the View class and provide all the functionality there

@Override
protected void onAnimationEnd() {
    super.onAnimationEnd();
    //Functionality here
}

This is the proper workaround for this issue, provide the functionality in the over-riden View -> onAnimationEnd(...) method as opposed to the onAnimationEnd(...) method of the AnimationListener attached to the Animation.

This works properly and there is no longer any flicker towards the end of the animation. Hope this helps

Solution 2:

From API 11, you can use the ObjectAnimator, which actually changes the view properties, i.e. in the case of a translation, the view will remain at the position it reaches after the animation.

ObjectAnimator objectAnimator= ObjectAnimator.ofFloat(mContent_container, "translationX", startX, endX);
objectAnimator.setDuration(1000);
objectAnimator.start();

More here.

Solution 3:

Soham's answer above works for me, although it's worth pointing out (since it wasn't immediately obvious to me when first reading this thread) that you can still get very nearly the same behavior as an animation listener by setting a separate listener on the view to be run at the end of your View's onAnimationStart() and onAnimationEnd().

For instance, if your code needs to disable a button for the duration of an animation:

Animation a = getAnimation(/* your code */);
a.setDuration(1000);
a.setAnimationListener(new AnimationListener() {
   @Override
   public void onAnimationStart(Animation arg0) {
     myButton.setEnabled(false);
   }

   @Override
   public void onAnimationEnd(Animation arg0) {
     myButton.setEnabled(true);
   }
});
someView.startAnimation(a);

Currently, someView doesn't know about myButton, and I'd like to keep it that way. You can just create some listener on your custom view class that gets called in the same fashion:

public final class SomeView extends View {
    // other code

    public interface RealAnimationListener {
      public void onAnimationStart();
      public void onAnimationEnd();
    }

    private RealAnimationListener mRealAnimationListener;

    public void setRealAnimationListener(final RealAnimationListener listener) {
      mRealAnimationListener = listener;
    }

    @Override
    protected void onAnimationStart() {
      super.onAnimationStart();
      if (mRealAnimationListener != null) {
         mRealAnimationListener.onAnimationStart();
      }
    }

    @Override
    protected void onAnimationEnd() {
      super.onAnimationEnd();
      if (mRealAnimationListener != null) {
         mRealAnimationListener.onAnimationEnd();
      }
    }
}

And then back in your other code (probably an Activity):

Animation a = getAnimation(/* your code */);
a.setDuration(1000);
someView.setRealAnimationListener(new RealAnimationListener() {
   @Override
   public void onAnimationStart() {
     myButton.setEnabled(false);
   }

   @Override
   public void onAnimationEnd() {
     myButton.setEnabled(true);
   }
});
someView.startAnimation(a);

This way you keep your components separated cleanly while still getting an AnimationListener that works.

Solution 4:

Using Soham's answer, here is an ImageView specific to fade animations:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;

/*
 * Custom view to prevent flickering on animation end
 * 
 * http://stackoverflow.com/questions/2650351/android-translateanimation-resets-after-animation
 */
public class FadeableImageView extends ImageView {

public FadeableImageView(Context context) {
    super(context);
}

public FadeableImageView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public FadeableImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
protected void onAnimationEnd() {
    super.onAnimationEnd();
    this.setVisibility(View.GONE);
}
}

And here is my animation code:

protected void startSplash() {
    final FadeableImageView splash = (FadeableImageView) findViewById(R.id.splash);

    Animation fadeOut = new AlphaAnimation(1, 0);
    fadeOut.setDuration(2000);
    splash.startAnimation(fadeOut);
}

Solution 5:

Get rid of setFillAfter and just use View.GONE in onAnimationEnd(). See here for a sample custom View that implements a sliding panel using a TranslateAnimation.