Cannot cast to AnimatedVectorDrawableCompat in Nougat

4.6k Views Asked by At

This is my build.gradle

    defaultConfig {
    ...
    minSdkVersion 21
    targetSdkVersion 26
    vectorDrawables.useSupportLibrary = true
}

and a part of the layout

<ImageView
    android:id="@+id/recents"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="?attr/selectableItemBackground"
    android:clickable="true"
    android:scaleType="fitCenter"
    app:srcCompat="@drawable/anim_test"/>

and the class cast:

val np = convertView.findViewById<ImageView>(R.id.recents)
val anim = np.drawable as AnimatedVectorDrawableCompat

This works as expected on Lolipop (sdk 21) but fails on Nougat saying:

android.graphics.drawable.AnimatedVectorDrawable cannot be cast to android.support.graphics.drawable.AnimatedVectorDrawableCompat

What I dont get is, why does it return an AnimatedVectorDrawableCompat on sdk level 21 at all when AnimatedVectorDrawable is already supported by the system. And why does it return the AnimatedVectorDrawable in Nougat in spite of specifying vectorDrawables.useSupportLibrary = true.

5

There are 5 best solutions below

1
On BEST ANSWER

Short answer:

Use AnimatedVectorDrawableCompat.registerAnimationCallback static method and it will do the job for you.

(drawable as Animatable).start()

AnimatedVectorDrawableCompat.registerAnimationCallback(
        drawable,
        object : Animatable2Compat.AnimationCallback() {
            override fun onAnimationEnd(drawable: Drawable?) {
                postOnAnimation {
                    (drawable as Animatable).start()
                }
            }
        })
   

Long answer:

I was having the same problem when I was trying to loop an animated vector drawable. Until I found out the support library returns different classes (AnimatedVectorDrawable and AnimatedVectorDrawableCompat) on different SDK levels.

It was not documented anywhere except this wonderful blog post of Nick Butcher:

https://medium.com/androiddevelopers/re-animation-7869722af206

He says:

Interestingly the support library currently uses the native version on API 24+ and the compat version prior despite the class being introduced in API 21. This enables it to supply bug fixes to APIs 21–23.

In the blog post the author also suggests other methods to work around this issue like using AnimatedVectorDrawableCompat#create method and setting the drawable in the runtime.

I recommend you to read the whole article.

Hope this helps.

0
On

Thanks to SafaOrhan for the working example.
I'll post the Java version with the imports.
I've used a class member variable to be able to stop the animation from another method.

import android.graphics.drawable.Drawable;
import android.graphics.drawable.Animatable;
import android.support.graphics.drawable.Animatable2Compat.AnimationCallback;
import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
import android.os.Handler;

public class MainActivity {
  private Animatable vectorAnimation;

  public void stopAnimation() {
    if (vectorAnimation != null) vectorAnimation.stop();
  }

  public void startAnimation() {
    Drawable vectorDrawable = imageView.getDrawable();
    vectorAnimation = (Animatable) vectorDrawable;
    AnimatedVectorDrawableCompat.registerAnimationCallback(vectorDrawable, new AnimationCallback() {
      public void onAnimationEnd(Drawable drawable) {
        new Handler().postDelayed(() -> vectorAnimation.start(), 1000);
      }
    });
    vectorAnimation.start();
  }
}
0
On

It is a little late, but I just had the similar issue and I resolved it as follows. Maybe it will help someone.

When we are using vectorDrawables for animations, we need to do following three things:

  1. Create an AnimatedVectorDrawableCompat from the vector drawable resource:
val drawable: AnimatedVectorDrawableCompat? =
          AnimatedVectorDrawableCompat.create(context, R.drawable.animated_vector)
  1. Cast this drawable to Animatable2Compat:
val animatable: Animatable2Compat = drawable as Animatable2Compat
  1. Now register for callback provided by the support
animatable.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
    override fun onAnimationEnd(drawable: Drawable?) {
      // Put code to execute after the animation is done
    }
  })

This is what I did. Please feel free to comment if there is a better approach.

0
On

Instead of making checks with API<21 you can cast to Animatable since both AnimatedVectorDrawable and AnimatedVectorDrawableCompat implements it

var anim = mImageView.drawable as Animatable
    anim.start()
0
On

I deal it like this:

public class MainActivity extends AppCompatActivity {
    ImageView img;
    Button show, play, stop;
    AnimatedVectorDrawableCompat anim_show, anim_play, anim_stop;
    Object canim_show, canim_play, canim_stop;

    static {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }

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

        show = findViewById(R.id.show);
        play = findViewById(R.id.play);
        stop = findViewById(R.id.stop);

        if (Build.VERSION.SDK_INT < 21 ) {
            anim_show = (AnimatedVectorDrawableCompat) getResources().getDrawable(R.drawable.xunfei_show_animated_vector);
            anim_play = (AnimatedVectorDrawableCompat) getResources().getDrawable(R.drawable.xunfei_play_animated_vector);
            anim_stop = (AnimatedVectorDrawableCompat) getResources().getDrawable(R.drawable.xunfei_stop_animated_vector);

        }else{
            canim_show = (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.xunfei_show_animated_vector);
            canim_play = (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.xunfei_play_animated_vector);
            canim_stop = (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.xunfei_stop_animated_vector);
        }

        show.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onClick(View view) {
                if (Build.VERSION.SDK_INT < 21) {
                    img.setImageDrawable(anim_show);
                    anim_show.start();
                } else {
                    img.setImageDrawable((AnimatedVectorDrawable) canim_show);
                    ((AnimatedVectorDrawable) canim_show).start();
                }

            }
        });
    }
}