android animation is not finished in onAnimationEnd
Asked Answered
L

12

70

It seems that an android animation is not truly finished when the onAnimationEnd event is fired although animation.hasEnded is set to true.

I want my view to change it's background drawable on the end of it's ScaleAnimation which it does, but you can clearly see that it is changed some miliseconds before it finishes. The problem is, that it flickers because the new background appears (=is) scaled for a short time until the animation really finishes.

Is there a way to get either the real end of the animation or just prevent the new background from beeing scaled this short period of time?

Thank you!


//EDIT: I'm using an AnimationListener to get the following call:

    @Override
public void onAnimationEnd(Animation animation)
{
    View view = (MyView) ((ExtendedScaleAnimation) animation).getView();

    view.clearAnimation();
    view.requestLayout();
    view.refreshBackground(); // <-- this is where the background gets changed
}
Lassiter answered 20/1, 2011 at 18:23 Comment(6)
Are you using an AnimationListener to get the OnAnimationEnd call, or some other way? Can you post the relavent code from your project so we have a better odea of whats going on?Upheaval
Sure, thank you! I'm editing the question to add the code and some details.Lassiter
Could you please help me ShadowMare, I have the same problem and don't know how to resolve thisCymene
Have you tried using the code provided below? That works for me. It basically lets the view handle the onAnimationEnd instead of the animation itself. Let me know if you need more assistance.Lassiter
Hi @ShadowMare, could you please help me with this because I have a similar problem.Cymene
Have you tried adding the OnAnimationEnd to the ImageView instead of the Animation itself? You have to handle the end of the animation there and it should work. I need more code for for further assistance.Lassiter
C
94

Here is the actual bug related to this issue http://code.google.com/p/android-misc-widgets/issues/detail?id=8

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.

Cinerary answered 24/2, 2011 at 21:5 Comment(9)
Could you please help me Soham, I have the same problem and don't know how to resolve this.Cymene
Sure Ana, are you applying the animation to an ImageView?Cinerary
Yes exactly, I am applaying the animation to an imageView. I have created the view class but i am never entering in onAnimationEndCymene
If you are programming for 3.0+ just use Property animationClockwise
And what about using content view layouts? In this case you can't use nested class and therefore parent class functionality, guess.Unbend
@Cinerary What about the case where the View object has to perform some animations?Hogtie
We can't view the bug at that link anymore, but this is still an issue in Android M, N...Gamosepalous
Still doesn't work. Acts the same exact way as if I added a listener. I can even add a breakpoint on the callback, and it gets hit mid-animation, way before it finishes. What the hell is wrong with Android development...?Houppelande
Remember that anonymous class derived from AnimationListener must either be abstract or implement abstract method.Trenna
H
32

I was abe to resolve this by calling clearAnimation() on the view being animated inside onAnimationEnd, that took away the flicker Its weird why would anyone have to do that, as onAnimationEnd callback should have been called only if the animation has already ended. But I guess the answer lies in the depth of Framework on how view/layout handles animation callback. For now take it as a hack-free solution, that just works.

        animation.setAnimationListener(new AnimationListener() {

        public void onAnimationEnd(Animation anim) {
            innerView.clearAnimation();   // to get rid of flicker at end of animation

            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams
            (innerBlockContainer.getWidth(), innerBlockContainer.getHeight());

            /* Update lp margin, left/top to update layout after end of Translation */
            ViewGroup parent_ofInnerView = (ViewGroup)innerView.getParent();
            vp.updateViewLayout(innerBlockContainer, lp);

        }

        public void onAnimationRepeat(Animation arg0) {}

        public void onAnimationStart(Animation arg0) {
        }

    });

     innerView.startAnimation(animation);
Hasidism answered 12/4, 2011 at 20:54 Comment(4)
That didn't help in my case. The suggestion from Soham did, however.Canonical
Wow! Simple one-line solution, worked like a charm! I just added the "view.clearAnimation();" before my "view.layout()" - and voila! :-)Maxma
I almost love you man! After 5 hours looking at code, wanted to get suicide;) You saved my life!Noncompliance
Although calling clearAnimation () will make another call to onAnimationEnd !Povertystricken
C
7

I had same issue and solved it using

view.clearAnimation();

before

view.startAnimation(anim);
Circinate answered 10/9, 2014 at 14:15 Comment(1)
call view.invalidate(); after setting the animation, it works for me and the animation listener comes back in to actionRodent
H
6

I had a similar problem and I used Soham's solution with custom view class.

It worked fine, but at the end, I've found a simpler solution that worked for me.

After calling the view.StartAnimation(animation), and before the next step in my program, I've added a short delay that will be long enough to let the animation finish, but short enough to be unnoticeable by the user:

new Handler().postDelayed(new Runnable() {
       @Override
       public void run() {
           nextStepInMyProgram();
       }
     }, 200);// delay in milliseconds (200)
Honorine answered 8/9, 2013 at 11:58 Comment(4)
A horrible solution but works without overriding classes, also only with post(Runnable), without postDelayed(Runnable, int)Intercollegiate
Massimo - post(Runnable) is not letting the animation to complete, so Doigen is correct postDelayed(Runnable) must be used. Just a little correction we can use delay = "animation.getDuration()"Southeaster
And this works with AnimatorSet setting the function in onAnimationStartConsueloconsuetude
This solution in addition to horrible is not complete : do not cover all the scenarios. If you have more animations fired after nextStepInMyProgram() you will have side-effectsPentimento
F
2

For some reason the onAnimationStart works properly, and the onAnimationEnd doesnt. So heres how I originally did it and what I changed:

Attempt 1 (flicker): a) Move image from 0px to 80px b) In onAnimationEnd, set the image's location to 80px

Attempt 2 (no flicker): a) In onAnimationStart, set the image's location to 80px b) Move the image from -80px to 0px

Hope that made sense. Basically I flipped the way I did it

Frederiksberg answered 8/2, 2011 at 19:11 Comment(1)
With the translate animation this is much better.Marsland
M
2

Try to use getAnimation() from your object:

public void onShowListBtnClick(View view)
    {
        rightPanel.startAnimation(AnimationUtils.loadAnimation(MainActivity.this, R.anim.slide_left));

        rightPanel.getAnimation().setAnimationListener(new Animation.AnimationListener() {    
            @Override
            public void onAnimationStart(Animation animation) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                // write your code here
            }
        });
}
Mcclimans answered 9/6, 2016 at 20:19 Comment(0)
G
1

An easy fix is to add one line to AnimationListener.onAnimationEnd():

@Override 
public void onAnimationEnd(Animation a) {
    a.setAnimationListener(null);
    …
}
Goldfish answered 29/6, 2015 at 14:18 Comment(0)
L
1

annimation can be also stopped on screen rotation. in this case onAnimationEnd() is not being called. my workaround:

 animation.setDuration(animationDuration);
 animation.setAnimationListener(new AnimationListenerAdapter() {...});
 view.startAnimation(animation);
 handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if(!animation.hasEnded()) {
                    // here you can handle this case
                }
            }
        }, animationDuration + 100);
Ligulate answered 31/7, 2015 at 9:42 Comment(0)
L
0

I had this issue because my Animation was not started in the main thread. This resulted in a duration of 0. However , the Animation did play correctly - it just called onAnimationEnd() immediately after execution.

Lagting answered 24/9, 2015 at 11:53 Comment(0)
N
0

If you are using repeat count as infinite, then onAnimationEnd would not get called. Check the documentation link

Noctambulous answered 28/3, 2018 at 10:19 Comment(0)
F
0

You can also use setUpdateListener, then check the current fraction of the animation progress and act accordingly.

Here's a Kotlin example for a fade-out animation which eventually makes the view gone from the layout:

view.animate()
        .alpha(0f)
        .setDuration(duration)
        .setUpdateListener { animation ->
            if (animation.animatedFraction > 0.99f) {
                view.visibility = View.GONE
            }
        }
        .start()
Fusilier answered 23/9, 2020 at 10:4 Comment(0)
S
-1

This worked for me:

@Override
public void onAnimationUpdate(ValueAnimator animation) {
    if (Float.compare(animation.getAnimatedFraction(),1.0f)) {
    // animation ended;
        //do stuff post animation
    }
}
Swoon answered 30/10, 2013 at 14:8 Comment(1)
This, too, may happen more than once, especially if the duration is long enough.Goldfish

© 2022 - 2024 — McMap. All rights reserved.