Search code examples
androidanimationandroid-animationanimatedvectordrawable

AnimatedVectorDrawableCompat looping animation using callback


I'm trying to implement an animation in my Android app using AnimatedVectorDrawableCompat, for compatibility for API >= 21.

I want the animation to loop for the duration of the Activity. I'm able to play the animation, and it will also loop fine on API >= 25. But when I run it on devices or emulators with API 21 through 24 I only see the animation once. If I set a breakpoint inside the callback method, I see that it executes the callback too, but the animation does not repeat.

I find that the animation is running on a different thread, as it does not block UI.

This is the method:

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

    imageView = findViewById(R.id.image_view1);
    final AnimatedVectorDrawableCompat anim = AnimatedVectorDrawableCompat.create(this, R.drawable.avd_pass_inside);

    imageView.setImageDrawable(anim);

    anim.registerAnimationCallback(new Animatable2Compat.AnimationCallback() {
        @Override
        public void onAnimationEnd(Drawable drawable) {

            anim.start();
        }
    });
    anim.start();
}

From what I read, using the Compat library should make this work for all API levels from 14 and up, but I don't even have to go there, as the rest of my app has requirements that puts it at mimimum 21.

Is there some (preferably non-hacky :) ) way to make this work consistently across these API levels? Is it a bug? Did I miss something?


Solution

  • As far as I can see this is a subtle difference between the system & compat versions. Compat seems to call the callback before its animations have been flagged as having ended. So the call to start() is ignored because it thinks it hasn't ended.

    The solution is the usual hack: post a Runnable to start it when the animations have finished.

    new Animatable2Compat.AnimationCallback() {
      @NonNull
      private final Handler fHandler = new Handler(Looper.getMainLooper());
    
      @Override
      public void onAnimationEnd(@NonNull Drawable drawable) {
        Animatable2Compat avd = (Animatable2Compat) drawable;
        fHandler.post(avd::start);
      }
    };