Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force usage of AnimatedVectorDrawableCompat in API 21 & 22 to use registerAnimationCallback

I am using an animated vector drawable for an app that supports Android API 19-26. In order to restart the animation (it's a custom circular loading animation) I use AnimatedVectorDrawable.registerAnimationCallback, to restart the animation in the onAnimationEnd callback. This works great on API >= 23 and due to AnimatedVectorDrawableCompat it also works on API 19.

However, it doesn't work on API 21 and 22, because the class AnimatedVectorDrawable is already present in these APIs, but the registerAnimationCallback method was only added in API 23. How can I force devices that run API 21 or 22 to use AnimatedVectorDrawableCompat instead of their AnimatedVectorDrawable class, so that I can use registerAnimationCallback?

Here is the method I wrote for starting the animation for different API versions (It's in Kotlin):

private fun startAnimation() {
  if (Build.VERSION.SDK_INT >= 23) {
    ((circular_progress.drawable as LayerDrawable)
        .findDrawableByLayerId(R.id.loading_circle) as AnimatedVectorDrawable).apply {
      registerAnimationCallback(@TargetApi(23)
      object : Animatable2.AnimationCallback() {
        override fun onAnimationEnd(drawable: Drawable?) {
          super.onAnimationEnd(drawable)
          [email protected]()
        }

        override fun onAnimationStart(drawable: Drawable?) = super.onAnimationStart(drawable)
      })
    }.start()
  } else if (Build.VERSION.SDK_INT >= 21) {
    ((circular_progress.drawable as LayerDrawable)
        .findDrawableByLayerId(R.id.loading_circle) as AnimatedVectorDrawable).apply {
      start()

      // No registerAnimationCallback here =( 

    }
  } else {
    ((circular_progress.drawable as LayerDrawable)
        .findDrawableByLayerId(R.id.loading_circle) as AnimatedVectorDrawableCompat).apply {
      registerAnimationCallback(object :
          Animatable2Compat.AnimationCallback() {
        override fun onAnimationEnd(drawable: Drawable?) {
          super.onAnimationEnd(drawable)
          [email protected]()
        }

        override fun onAnimationStart(drawable: Drawable?) = super.onAnimationStart(drawable)
      })
    }.start()
  }
}
like image 285
janosch Avatar asked Sep 22 '17 09:09

janosch


1 Answers

Ok I found the solution. Previously I used this LayerDrawable, which was defined in xml as layer_list.xml:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/normal_drawable"/>
    <item android:id="@+id/loading_circle"
    android:drawable="@drawable/animated_vector_drawable"/>
</layer-list>

The first item is a normal vector drawable, the second is an animated vector drawable:

<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:drawable="@drawable/...">

    <target .../>

    <target .../>

</animated-vector>

I then set the LayerDrawable to an ImageView like so:

imageView.setImageResource(R.drawable.layer_list)

The problem here was that the animated-vector drawable was internally instantiated as an AnimatedVectorDrawable in the API versions 21 and 22, but it should be an AnimatedVectorDrawableCompat.

The solution is to instantiate the LayerDrawable, the normal drawable and the vector drawable programmatically:

val normalDrawable = ContextCompat.getDrawable(context, R.drawable.normal_drawable)
val animatedDrawable = AnimatedVectorDrawableCompat.create(context, R.drawable.animated_vector_drawable)
val layeredList = LayerDrawable(arrayOf(normalDrawable, animatedDrawable))

imageView.setImageDrawable(layeredList)

Here the animated drawable is explicitly instantiated as an AnimatedVectorDrawableCompat so using the method registerAnimationCallback is possible:

animatedDrawable.registerAnimationCallback(...)
like image 149
janosch Avatar answered Sep 22 '22 04:09

janosch