androidanimatedvectordrawable

Android Animated Vector Drawable doesn't work properly


I have the following ImageView

<androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/image_header_toggle"
        android:layout_width="28dp"
        android:layout_height="28dp"
        android:layout_marginEnd="10dp"
        app:layout_constraintBottom_toBottomOf="@+id/text_header_name"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@+id/text_header_name"
        android:src="@drawable/ic_header_off_to_on"
        app:tint="?attr/colorPrimary"/>

and two animated drawables that I made through https://shapeshifter.design/

ic_header_off_to_on.xml

<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">
    <aapt:attr name="android:drawable">
        <vector
            android:name="vector"
            android:width="24dp"
            android:height="24dp"
            android:viewportWidth="24"
            android:viewportHeight="24">
            <group
                android:name="group"
                android:pivotX="12"
                android:pivotY="12">
                <path
                    android:name="path_1"
                    android:pathData="M 15.41 16.58 L 10.83 12 L 15.41 7.41 L 14 6 L 8 12 L 14 18 L 15.41 16.58 Z"
                    android:fillColor="#000"
                    android:strokeWidth="1"/>
            </group>
        </vector>
    </aapt:attr>
    <target android:name="group">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:propertyName="rotation"
                android:duration="300"
                android:valueFrom="0"
                android:valueTo="-90"
                android:valueType="floatType"
                android:interpolator="@android:interpolator/fast_out_slow_in"/>
        </aapt:attr>
    </target>
</animated-vector>

ic_header_on_to_off.xml

<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">
    <aapt:attr name="android:drawable">
        <vector
            android:name="vector"
            android:width="24dp"
            android:height="24dp"
            android:viewportWidth="24"
            android:viewportHeight="24">
            <group
                android:name="group"
                android:pivotX="12"
                android:pivotY="12"
                android:rotation="270">
                <path
                    android:name="path_1"
                    android:pathData="M 15.41 16.58 L 10.83 12 L 15.41 7.41 L 14 6 L 8 12 L 14 18 L 15.41 16.58 Z"
                    android:fillColor="#000"
                    android:strokeWidth="1"/>
            </group>
        </vector>
    </aapt:attr>
    <target android:name="group">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:propertyName="rotation"
                android:duration="300"
                android:valueFrom="270"
                android:valueTo="0"
                android:valueType="floatType"
                android:interpolator="@android:interpolator/fast_out_slow_in"/>
        </aapt:attr>
    </target>
</animated-vector>

and this code that I use to toggle the icons

val res = if (isExpanded) R.drawable.ic_header_off_to_on else R.drawable.ic_header_on_to_off
    
image_header_toggle.setImageResource(res)
(view.image_header_toggle.drawable as? AnimatedVectorDrawable)?.start()
image_header_toggle.setOnClickListener { isExpanded = !isExpanded }

The off_to_on animation seems to play nicely but the on_to_off one doesn't, the image simply gets replace. It looks like this >_<


Solution

  • I just found this question and tried something out. A more elegant solution is to remove your Kotlin code with mine. This will achieve a smooth transition and you even can change the duration of it. Instead of needing 2 drawables you can just use one. If you are using Fragment instead of Activity, don't forget to set view.findViewById instead of findViewById:

    class MainActivity : AppCompatActivity() {
    
        private var isExpanded = true
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
    
            val imageView = findViewById<ImageView>(R.id.image_header_toggle)
    
            imageView.setOnClickListener {
                if (!isExpanded) {
                    imageView.animate().apply {
                    duration = 500
                    rotation(0f)
                    isExpanded = true}
                }else{
                    imageView.animate().apply {
                    duration = 500
                    rotation(-90f)
                    isExpanded = false}
                }
            }
        }
    }