21

I'm creating a game and I would like to display a simple "score"-animation to the player when credits are given to him. This is the view I throw onto the screen:

public class Score extends FrameLayout {

  public Score(Context context, int score) {
    super(context);
    TextView txt = new TextView(context);
    txt.setText(String.valueOf(score).toUpperCase());  
    addView(txt);
    Animation anim = AnimationUtils.loadAnimation(context, R.anim.score);
    startAnimation(anim);
    anim.setAnimationListener(animationListener);
  }
  private void Remove(){

    ViewGroup parent = (ViewGroup)getParent();  
    parent.removeView(this);

  } 
  private AnimationListener animationListener = new AnimationListener() {

    @Override
    public void onAnimationEnd(Animation animation) {

      Remove();
    }  
  };
}

This code actually works pretty well as long as there is only ONE score animation on screen at any given time. If the player scores again, before the last score was removed, the app crashes - probably because the second score gets the event to remove itself during animation.. Is this a bad practice of using Animation? How would you guys handle this?

Svenhelge
  • 211
  • 1
  • 2
  • 3

3 Answers3

37

I also found that when removing a view from its parent after applying an animation to this view (using onAnimationEnd) crashes with NPE on the dispatchDraw of the parent.

The only solution I found is to trigger the removal inside a post call. Normally all UI modification must be done on the UI thread, so I added a runOnUiThread call on the activity, but it could be useless (it works for me without that).

Animation animation = AnimationUtils.loadAnimation(parentView.getContext(), animationId);
animation.setAnimationListener(new AnimationListener() {
    public void onAnimationStart(Animation paramAnimation) { }
    public void onAnimationRepeat(Animation paramAnimation) { }
    public void onAnimationEnd(Animation paramAnimation) { 
        // without the post method, the main UI crashes if the view is removed 
        parentView.post(new Runnable() {
            public void run() {
                // it works without the runOnUiThread, but all UI updates must 
                // be done on the UI thread
                activity.runOnUiThread(new Runnable() {
                    public void run() {
                        parentView.removeView(view);
                    }
                });
            }
        });
    }
});

view.setVisibility(visibility());
view.startAnimation(animation);
wInd
  • 391
  • 3
  • 5
  • 13
    the `runOnUiThread` is not necessary. Doing a post to a view will make the runnable run in the ui thread. – Macarse Jul 09 '11 at 15:19
  • 1
    Thanks, I ran into this same error and posting to the view allowed me to remove the views. It's _weird_ that an animation listener isn't running in the ui thread... – Kenny Wyland Sep 17 '11 at 01:59
  • 5
    i think it is already running on the ui thread. A possible reason that post() works (this is just a guess) is that if you remove the view directly in the animation listener methods, they will be called before the draw logic executes and you will encounter the NPE. For example each view may have its anim state updated before drawing, and if the anim is finished your listener methods get called. When you post a runnable it will be queued by the looper which will probably not process any messages until after the view hierarcy update / draw logic has finished for this frame. Could all be ui thread – Dori Dec 09 '11 at 14:42
  • I had an animated view, which was triggered after the user deletes an entry in a list. It crashed when multiple of those view were triggered. This answer solved my problem, deleting the view even WITHOUT "runOnUiThread". – FonzTech Mar 25 '18 at 19:22
1

For Kotlin language and using View Binding, this my solution:

      animation.setAnimationListener(object : Animation.AnimationListener {
            override fun onAnimationStart(p0: Animation?) {
            }

            override fun onAnimationEnd(p0: Animation?) {
                mViewBinding
                    .parentViewContainer
                    .post {
                         //The code below will be run on UI thread.
                         mViewBinding
                            .parentViewContainer
                            .removeView(view)
                    }
            }

            override fun onAnimationRepeat(p0: Animation?) {
            }
        })
Tri Dawn
  • 540
  • 6
  • 11
0

When using AnimationUtils.loadAnimation view.clearAnimation() solved my problem

Samir Bhatt
  • 3,041
  • 2
  • 25
  • 39