AnimatedVectorDrawable

Jong_Han·2022년 1월 3일
0

Android

목록 보기
5/9

AnimatedVectorDrawable?

AnimatedVectorDrawable이란, 애니메이션이 추가된 VectorDrawable 입니다.

pathData로 이루어진 Drawable에 자연스러운 애니메이션 효과를 추가할 수 있습니다.

대표적인 예시로 다음웹툰의 좌상단에 위치한 이미지가 있습니다.

다음웹툰의 UX에 관한 글 : https://brunch.co.kr/@kakao-it/279

이번에는 다음웹툰을 참고해서 H에서 Z로 변하는 AnimatedVectorDrawable을 구현해보도록 하겠습니다.

1. Vector 아이콘 만들기

우선 사용할 벡터 아이콘이 필요하기 때문에, 각각 만들어주었습니다.

벡터 아이콘은 M, L, C, Z과 좌표 커맨드로 작성한 pathData로 이루어져 있으며, 해당 path값은 values/paths.xml 파일을 만들어 따로 관리하였습니다.

paths.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="path_h_1">M 0 0 L 0 24 L 6 24 L 6 0 Z</string>
    <string name="path_h_2">M 24 0 L 24 24 L 18 24 L 18 0 Z</string>
    <string name="path_h_3">M 0 9 L 24 9 L 24 15 L 0 15 Z</string>

    <string name="path_z_3">M 24 0 L 18 0 L 0 24 L 6 24 Z</string>
    <string name="path_z_1">M 0 0 L 24 0 L 18 8 L 0 8 Z</string>
    <string name="path_z_2">M 0 24 L 6 16 L 24 16 L 24 24 Z</string>
</resources>

각 커맨드의 의미는 다음과 같습니다.

  • M : 시작점을 의미 합니다. ( M 0 0 → 0,0, 좌표를 시작점으로 지정함)
  • L : 직전 좌표부터 지정한 좌표까지 직선을 그립니다.
  • C : 직전 좌표부터 지정한 좌표까지 곡선을 그립니다.
  • Z : 직전 좌표에서 시작점으로 돌아갑니다.

위 4가지 커맨드를 조합해서 원하는 벡터 이미지를 그릴 수 있습니다.

ic_h.xml

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="300dp"
    android:height="300dp"
    android:viewportHeight="30"
    android:viewportWidth="30">

    <group
        android:name="rotate_1"
        android:pivotY="12.0"
        android:pivotX="12.0">
        <path
            android:name="h_1"
            android:pathData="@string/path_h_1"
            android:fillColor="@color/purple_200"/>
        <path
            android:name="h_2"
            android:pathData="@string/path_h_2"
            android:fillColor="@color/purple_200"/>
        <path
            android:name="h_3"
            android:pathData="@string/path_h_3"
            android:fillColor="@color/purple_200"/>
    </group>

</vector>

ic_z.xml

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="300dp"
    android:height="300dp"
    android:viewportHeight="30"
    android:viewportWidth="30">

    <group>
        <path
            android:name="z_1"
            android:pathData="@string/path_z_1"
            android:fillColor="@color/teal_200"/>
        <path
            android:name="z_2"
            android:pathData="@string/path_z_2"
            android:fillColor="@color/teal_200"/>
        <path
            android:name="z_3"
            android:pathData="@string/path_z_3"
            android:fillColor="@color/teal_200"/>
    </group>

</vector>

2. Animation 정의

h_to_z.xml

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/ic_h">
    <target android:name="h_1" >
        <aapt:attr name="android:animation">
            <set
                android:fillAfter = "true"
                android:ordering="together">
                <objectAnimator
                    android:propertyName="pathData"
                    android:valueType="pathType"
                    android:valueFrom="@string/path_h_1"
                    android:valueTo="@string/path_z_1"
                    android:duration="400" />
                <objectAnimator
                    android:propertyName="fillColor"
                    android:valueFrom="@color/purple_200"
                    android:valueTo="@color/teal_200"
                    android:duration="400" />
            </set>
        </aapt:attr>
    </target>
    <target android:name="h_2" >
        <aapt:attr name="android:animation">
            <set
                android:fillAfter = "true"
                android:ordering="together">
                <objectAnimator
                    android:propertyName="pathData"
                    android:valueType="pathType"
                    android:valueFrom="@string/path_h_2"
                    android:valueTo="@string/path_z_2"
                    android:duration="400" />
                <objectAnimator
                    android:propertyName="fillColor"
                    android:valueFrom="@color/purple_200"
                    android:valueTo="@color/teal_200"
                    android:duration="400" />
            </set>
        </aapt:attr>
    </target>
    <target android:name="h_3" >
        <aapt:attr name="android:animation">
            <set
                android:fillAfter = "true"
                android:ordering="together">
                <objectAnimator
                    android:propertyName="pathData"
                    android:valueType="pathType"
                    android:valueFrom="@string/path_h_3"
                    android:valueTo="@string/path_z_3"
                    android:duration="400" />
                <objectAnimator
                    android:propertyName="fillColor"
                    android:valueFrom="@color/purple_200"
                    android:valueTo="@color/teal_200"
                    android:duration="400" />
            </set>
        </aapt:attr>
    </target>
</animated-vector>

<animated-vector> 태그로 h에서 z로 변하는 AnimatedVectorDrawable을 작성합니다.

drawable에는 h 아이콘을 지정해주고, 하위 태그 <target> 아래에 애니메이션을 지정합니다.

이때, target의 name은 VectorDrawable에서 지정한 path의 name과 같게 지정해야 합니다.

 <target android:name="h_1" >
        <aapt:attr name="android:animation">
            <set
                android:fillAfter = "true"
                android:ordering="together">
                <objectAnimator
                    android:propertyName="pathData"
                    android:valueType="pathType"
                    android:valueFrom="@string/path_h_1"
                    android:valueTo="@string/path_z_1"
                    android:duration="400" />
                <objectAnimator
                    android:propertyName="fillColor"
                    android:valueFrom="@color/purple_200"
                    android:valueTo="@color/teal_200"
                    android:duration="400" />
            </set>
        </aapt:attr>
    </target>

예를 들어 위 코드는 h_1 name을 가진 path에 <set> 아래 지정된 애니메이션을 지정하는 코드 입니다.

위와 같은 방식으로 z에서 h로 변하는 AnimatedVectorDrawable도 작성합니다.

애니메이션 실행

activity_vector_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".vectordrawable.VectorDrawableActivity">

    <ImageView
        android:id="@+id/vector_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/h_to_z"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

ImageView를 두고 정의한 AnimatedVectorDrawable를 지정합니다.

VectorDrawableActivity.kt

class VectorDrawableActivity : AppCompatActivity() {

    private val image by lazy { findViewById<ImageView>(R.id.vector_img) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_vector_drawable)

        image.setOnClickListener {

            if ( image.tag == null ) {
                image.setImageResource(R.drawable.h_to_z)
                (image.drawable as AnimatedVectorDrawable).start()
                image.tag = 1
            } else {
                image.setImageResource(R.drawable.z_to_h)
                (image.drawable as AnimatedVectorDrawable).start()
                image.tag = null
            }

        }

    }
}

이미지뷰를 클릭할 때마다 h에서 z로, z에서 h로 변하도록 코드를 작성하였습니다.

이미지를 클릭하면 애니메이션이 실행되며 변화하는 것을 볼 수 있습니다.

주의할 점

path를 이용한 애니메이션을 구현하면 path가 자연스럽게 변화하는 것을 볼 수 있습니다.

이를 Path morphing이라고 하며, 구현 시 주의해야할 점이 몇 가지 있습니다.

주의할 점은 다음과 같습니다.

  1. A path에서 B path로 변화할 때는 같은 수의 command가 필요함

    ex) M .. L .. L .. L .. Z → M .. L .. Z 불가능

  2. 각 대응되는 커맨드는 같은 커맨드여야만 함

    ex) M .. C .. Z → M .. L .. Z 불가능

  3. Path morphing은 1:1 대응이여야만 함

    ex) 3개의 pathData를 이용한 벡터 이미지에서 2개의 pathData를 이용한 벡터 이미지로의 path morphing은 불가능

    다음웹툰 발표를 보면 모든 도형을 6등분 했다고 하는데, 이것 때문이 아닐까...라는 생각이 듭니다.

profile
안드로이드 개.....발자?

0개의 댓글