Android Animation (Part 3)

by KINH HOANG` HON
288 views

Xin chào các bạn, bài tiếp theo trong series về animation của mình đó là giới thiệu cho các bạn về AnimatedVectorDrawable.

VectorDrawable

Thông thường để có những hình ảnh tương tích với các màn hình khác nhau trong thiết bị Android thì bạn sẽ tạo ra các hình ảnh như hdpi, mdpi, xhdpi, xxhdpi, xxxhdpi.
Để tối ưu cho phần này thì trong API 21, Android đã phát hành VectorDrawable giúp thay thế nhiều ảnh .png thành một đồ hoạ vector được tạo bằng xml.
VectorDrawable bao gồm các điểm, đường thằng, đường cong, màu sắc…khi co giãn không làm ảnh hưởng tới chất lượng của ảnh. Đó thực sự là một điểm mạnh của VectorDrawable.

Ví dụ VectorDrawable cho numeric_0 ở bài trước như dưới đây:

<!-- drawable/numeric_0_vector.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path
        android:fillColor="#000"
        android:pathData="M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3H19M11,7A2,2 0 0,0 9,9V15A2,2 0 0,0 11,17H13A2,2 0 0,0 15,15V9A2,2 0 0,0 13,7H11M11,9H13V15H11V9Z" />
</vector>

cú pháp:
<vector>: xác định một vector drawable cần vẽ.
android:width & android:height: kích thước chiều rộng, chiều cao của hình dạng vector.
android:viewportWidth & android:viewportHeight: khung cửa sổ để vẽ hình dạng vector.
<path>: bên trong thẻ <vector>, xác định đường dẫn để vẽ.
android:fillColor: màu bạn sử dụng
android:pathData: các thuộc tính để vẽ và theo bộ quy tắc dưới đây:

  • M: di chuyển điểm vẽ đến tọa độ x, y (M x y).
  • L: vẽ từ điểm hiện tại đến điểm x, y (L x y).
  • H: vẽ đường ngang từ điểm hiện tại đến điểm có tọa độ x (H x).
  • V: vẽ đường thẳng đứng đến điểm có tọa độ y (V y).
  • C: vẽ đường cong cubic-bezier từ điểm hiện tại x0, y0 đến điểm x, y. Điểm đầu đường cong tiếp tuyến với đường thẳng x0,y0, x1, y1. Điểm thứ 2 của đường cong tiếp tuyến với tường x,y, x2, y2 C x1 y1, x2 y2, x, y.
  • S: vẽ đường cong trơn từ điểm hiện tại x0, y0 đến điểm x, y trong đó điểm đầu tiếp tuyến với đường x0,y0, x2, y2 S x2 y2, x y.
  • Q: vẽ đường cong cubic-bezier từ điểm hiện tại x0, y0 đến điểm x, y. điểm đầu đường cong tiếp tuyến với đường thẳng x0,y0, x1, y1 điểm thứ 2 của đường cong tiếp tuyến với tường x,y, x1, y1 C x1 y1, x y.
  • T: vẽ đường cong cubic-bezier, từ điểm hiện tại đến điểm x,y (T x y).
  • A: vẽ cung tròn.
  • Z: đóng đường vẽ.
  • Ngoài ra, chúng ta còn các cú pháp khác nữa. Bạn có thể tham khảo ở đây

AnimatedVectorDrawable

Để giới thiệu cho các bạn về AnimatedVectorDrawable thì tôi sẽ làm giống animation của bài trước, nhưng sẽ sử dụng AnimatedVectorDrawable để cho các bạn hình dung về nó dễ hơn.
Như bài trước thì chúng ta cần 3 ảnh png: ic_numeric_0.png, ic_numeric_1.png, ic_numeric_2.png và các ảnh cho các màn hình khác nhau trong Android: hdpi, mhdpi, xhdpi, xxhdpi, xxxhdpi là có tới khoảng 15 hình ảnh tất cả phải không?.
Ở bài này thì các bạn cần chuẩn bị 3 ảnh VectorDrawable cho mình nhé.
Bạn có thể tìm kiếm ở đây .
Mình sẽ sử dụng 3 ảnh VectorDrawable của bài trước.

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="24dp"
    android:width="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path android:fillColor="#000" android:pathData="M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3H19M11,7A2,2 0 0,0 9,9V15A2,2 0 0,0 11,17H13A2,2 0 0,0 15,15V9A2,2 0 0,0 13,7H11M11,9H13V15H11V9Z" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="24dp"
    android:width="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path android:fillColor="#000" android:pathData="M14,17H12V9H10V7H14M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3Z" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="24dp"
    android:width="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path android:fillColor="#000" android:pathData="M15,11C15,12.11 14.1,13 13,13H11V15H15V17H9V13C9,11.89 9.9,11 11,11H13V9H9V7H13A2,2 0 0,1 15,9M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3Z" />
</vector>

Chú ý: AnimatedVectorDrawable yêu cầu bạn sử dụng các hình ảnh tương thích với nhau (bạn hiểu đơn giản là android:pathData của các hình ảnh phải có cùng các lệnh, theo cùng một thứ tự và có cùng số lượng tham số cho mỗi lệnh…)
Các ảnh bên trên sẽ không tương thích nên bài viết này mình sẽ sử dụng ShapeShifter là một ứng dụng web được tạo bởi Alex Lockwood, giúp cho các ảnh .svg tương thích với nhau.
Sau khi mình sử dụng ShapeShifter thì sẽ được kết qủa như dưới đây animated_vector.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">
    <aapt:attr name="android:drawable">
        <vector
            android:name="vector"
            android:width="96dp"
            android:height="96dp"
            android:viewportWidth="24"
            android:viewportHeight="24">
            <path
                android:name="path"
                android:fillColor="#000"
                android:pathData="M 19 3 C 19.53 3 20.039 3.211 20.414 3.586 C 20.789 3.961 21 4.47 21 5 L 21 19 C 21 19.53 20.789 20.039 20.414 20.414 C 20.039 20.789 19.53 21 19 21 L 5 21 C 4.47 21 3.961 20.789 3.586 20.414 C 3.211 20.039 3 19.53 3 19 L 3 5 C 3 4.47 3.211 3.961 3.586 3.586 C 3.961 3.211 4.47 3 5 3 L 19 3 M 11 7 C 10.47 7 9.961 7.211 9.586 7.586 C 9.211 7.961 9 8.47 9 9 L 9 15 C 9 15.53 9.211 16.039 9.586 16.414 C 9.961 16.789 10.47 17 11 17 L 13 17 C 13.53 17 14.039 16.789 14.414 16.414 C 14.789 16.039 15 15.53 15 15 L 15 9 C 15 8.47 14.789 7.961 14.414 7.586 C 14.039 7.211 13.53 7 13 7 L 11 7 M 11 9 L 13 9 L 13 15 L 11 15 L 11 9 Z" />
        </vector>
    </aapt:attr>
    <target android:name="path">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="1000"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="pathData"
                    android:valueFrom="M 19 3 C 19.53 3 20.039 3.211 20.414 3.586 C 20.789 3.961 21 4.47 21 5 L 21 19 C 21 19.53 20.789 20.039 20.414 20.414 C 20.039 20.789 19.53 21 19 21 L 5 21 C 4.47 21 3.961 20.789 3.586 20.414 C 3.211 20.039 3 19.53 3 19 L 3 5 C 3 4.47 3.211 3.961 3.586 3.586 C 3.961 3.211 4.47 3 5 3 L 19 3 M 11 7 C 10.47 7 9.961 7.211 9.586 7.586 C 9.211 7.961 9 8.47 9 9 L 9 15 C 9 15.53 9.211 16.039 9.586 16.414 C 9.961 16.789 10.47 17 11 17 L 13 17 C 13.53 17 14.039 16.789 14.414 16.414 C 14.789 16.039 15 15.53 15 15 L 15 9 C 15 8.47 14.789 7.961 14.414 7.586 C 14.039 7.211 13.53 7 13 7 L 11 7 M 11 9 L 13 9 L 13 15 L 11 15 L 11 9 Z"
                    android:valueTo="M 19 3 C 19.53 3 20.039 3.211 20.414 3.586 C 20.789 3.961 21 4.47 21 5 L 21 19 C 21 19.53 20.789 20.039 20.414 20.414 C 20.039 20.789 19.53 21 19 21 L 5 21 C 4.47 21 3.961 20.789 3.586 20.414 C 3.211 20.039 3 19.53 3 19 L 3 5 C 3 4.47 3.211 3.961 3.586 3.586 C 3.961 3.211 4.47 3 5 3 L 19 3 M 11 7 C 10.47 7 9.961 7.211 9.586 7.586 C 9.211 7.961 9 8.47 9 9 L 9 15 C 9 15.53 9.211 16.039 9.586 16.414 C 9.961 16.789 10.47 17 11 17 L 13 17 C 13.53 17 14.039 16.789 14.414 16.414 C 14.789 16.039 15 15.53 15 15 L 15 9 C 15 8.47 14.789 7.961 14.414 7.586 C 14.039 7.211 13.53 7 13 7 L 11 7 M 11 9 L 13 9 L 13 15 L 11 15 L 11 9 Z"
                    android:valueType="pathType" />
                <objectAnimator
                    android:duration="1000"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="pathData"
                    android:startOffset="1000"
                    android:valueFrom="M 14 17 L 12 17 L 12 9 L 10 9 L 10 7 L 14 7 M 19 3 L 5 3 C 4.47 3 3.961 3.211 3.586 3.586 C 3.211 3.961 3 4.47 3 5 L 3 19 C 3 19.53 3.211 20.039 3.586 20.414 C 3.961 20.789 4.47 21 5 21 L 19 21 C 19.53 21 20.039 20.789 20.414 20.414 C 20.789 20.039 21 19.53 21 19 L 21 5 C 21 4.47 20.789 3.961 20.414 3.586 C 20.039 3.211 19.53 3 19 3 Z"
                    android:valueTo="M 14 17 L 12 17 L 12 9 L 10 9 L 10 7 L 14 7 M 19 3 L 5 3 C 4.47 3 3.961 3.211 3.586 3.586 C 3.211 3.961 3 4.47 3 5 L 3 19 C 3 19.53 3.211 20.039 3.586 20.414 C 3.961 20.789 4.47 21 5 21 L 19 21 C 19.53 21 20.039 20.789 20.414 20.414 C 20.789 20.039 21 19.53 21 19 L 21 5 C 21 4.47 20.789 3.961 20.414 3.586 C 20.039 3.211 19.53 3 19 3 Z"
                    android:valueType="pathType" />
                <objectAnimator
                    android:duration="1000"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="pathData"
                    android:startOffset="2000"
                    android:valueFrom="M 15 11 C 15 12.11 14.1 13 13 13 L 11 13 L 11 15 L 15 15 L 15 17 L 9 17 L 9 13 C 9 11.89 9.9 11 11 11 L 13 11 L 13 9 L 9 9 L 9 7 L 13 7 C 13.53 7 14.039 7.211 14.414 7.586 C 14.789 7.961 15 8.47 15 9 M 19 3 L 5 3 C 4.47 3 3.961 3.211 3.586 3.586 C 3.211 3.961 3 4.47 3 5 L 3 19 C 3 19.53 3.211 20.039 3.586 20.414 C 3.961 20.789 4.47 21 5 21 L 19 21 C 19.53 21 20.039 20.789 20.414 20.414 C 20.789 20.039 21 19.53 21 19 L 21 5 C 21 4.47 20.789 3.961 20.414 3.586 C 20.039 3.211 19.53 3 19 3 Z"
                    android:valueTo="M 15 11 C 15 12.11 14.1 13 13 13 L 11 13 L 11 15 L 15 15 L 15 17 L 9 17 L 9 13 C 9 11.89 9.9 11 11 11 L 13 11 L 13 9 L 9 9 L 9 7 L 13 7 C 13.53 7 14.039 7.211 14.414 7.586 C 14.789 7.961 15 8.47 15 9 M 19 3 L 5 3 C 4.47 3 3.961 3.211 3.586 3.586 C 3.211 3.961 3 4.47 3 5 L 3 19 C 3 19.53 3.211 20.039 3.586 20.414 C 3.961 20.789 4.47 21 5 21 L 19 21 C 19.53 21 20.039 20.789 20.414 20.414 C 20.789 20.039 21 19.53 21 19 L 21 5 C 21 4.47 20.789 3.961 20.414 3.586 C 20.039 3.211 19.53 3 19 3 Z"
                    android:valueType="pathType" />
            </set>
        </aapt:attr>
    </target>
</animated-vector>

Bài viết tiếp theo mình sẽ giới thiệu kỹ hơn đến các bạn về ShapeShifter.
Còn giờ thì hãy chạy để xem kết quả của bạn vừa làm với những dòng code dưới đây:

val animatedVectorDrawableCompat =
            AnimatedVectorDrawableCompat.create(this, R.drawable.animated_vector)
        val imageView = (findViewById<ImageView>(R.id.numeric_image)).apply {
            setImageDrawable(animatedVectorDrawableCompat)
        }
        animatedVectorDrawableCompat?.registerAnimationCallback(object :
            Animatable2Compat.AnimationCallback() {
            override fun onAnimationEnd(drawable: Drawable?) {
                imageView.post { animatedVectorDrawableCompat.start() }
            }
        })
        animatedVectorDrawableCompat?.start()
AnimatedVectorDrawable

Mình rất mong được các bạn đón đọc và để lại lời bình luận để mình cải thiện hơn nữa <3

Link tham khảo:

  • https://developer.android.com/reference/android/graphics/drawable/VectorDrawable
  • https://developer.android.com/guide/topics/graphics/vector-drawable-resources

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.