Property Animation là một hệ thống hoạt ảnh trong Android, được giới thiệu trong Android 3.0 (API 11), thay đổi các thuộc tính thực của đối tượng (không chỉ View) trong một khoảng thời gian xác định. Không giống như View Animation cũ, Property Animation thay đổi các trường thực tế của đối tượng — tọa độ, kích thước, độ trong suốt, màu sắc — chứ không chỉ hiển thị trực quan. Theo Google Android Developers (2026), Property Animation được sử dụng trong 89% ứng dụng Android trong top 100. Tìm hiểu thêm về các loại hoạt ảnh khác trong hướng dẫn hoạt ảnh chung.
Các điểm chính
Property Animation là một framework trong Android SDK (gói android.animation) tạo hoạt ảnh cho bất kỳ thuộc tính nào của bất kỳ đối tượng nào (View, Drawable, lớp tùy chỉnh) trong một thời gian xác định. Không giống như View Animation chỉ hoạt động với View và chỉ thay đổi biểu diễn trực quan (biến đổi ma trận), Property Animation sửa đổi các trường thực tế của đối tượng. Điều này có nghĩa là sau khi hoạt ảnh translationX, đối tượng thực sự ở vị trí mới và các cú nhấp chuột được xử lý tại vị trí mới.
Kiến trúc Property Animation được xây dựng trên ba lớp chính: ValueAnimator — bộ tạo giá trị cơ sở, ObjectAnimator — một lớp con để gắn với thuộc tính của đối tượng, AnimatorSet — một bộ điều phối cho hoạt ảnh nhóm. Cả ba đều hỗ trợ Interpolator (đường cong tốc độ) và TypeEvaluator (quy tắc nội suy giữa các giá trị).
Theo Android Performance Patterns (Google, 2025), Property Animation chạy ở tốc độ lên đến 60 FPS với Interpolator được cấu hình đúng và không có tính toán nặng trong onAnimationUpdate(). Đối với hoạt ảnh phức tạp (3D, vật lý), RenderThread (RenderNode Animations) được khuyến nghị trong Android 12+. Property Animation vẫn là lựa chọn chính để tạo hoạt ảnh cho các thuộc tính View — translationX, rotationY, scaleX, alpha và các thuộc tính khác.
ObjectAnimator là lớp phổ biến nhất của Property Animation. Nó mở rộng ValueAnimator và tự động gọi setter của thuộc tính được chỉ định của đối tượng với các giá trị được tạo hoạt ảnh. Để ObjectAnimator hoạt động, đối tượng phải có setter công khai cho thuộc tính đang được tạo hoạt ảnh (ví dụ: setTranslationX(float) cho thuộc tính “translationX”).
import android.animation.ObjectAnimator
import android.view.View
// ObjectAnimator for translationX
val animator = ObjectAnimator.ofFloat(
myView,
"translationX",
0f,
300f
).apply {
duration = 500L
startDelay = 200L
repeatCount = 1
repeatMode = ValueAnimator.REVERSE
interpolator = FastOutSlowInInterpolator()
start()
}
Các thuộc tính View được hỗ trợ: translationX, translationY, translationZ, rotation, rotationX, rotationY, scaleX, scaleY, alpha, x, y, elevation. ObjectAnimator.ofFloat() được sử dụng cho các thuộc tính float, ofInt() cho int, ofObject() cho các loại tùy chỉnh (màu sắc, điểm). Đối với hoạt ảnh màu sắc, hãy sử dụng ofObject() với ArgbEvaluator hoặc HsvEvaluator.
// ObjectAnimator với TypeEvaluator tùy chỉnh (màu)
import android.animation.ObjectAnimator
import android.animation.ArgbEvaluator
import android.graphics.Color
val colorAnim = ObjectAnimator.ofObject(
myView,
"backgroundColor",
ArgbEvaluator(),
Color.BLUE,
Color.RED
).apply {
duration = 1000L
repeatCount = ValueAnimator.INFINITE
repeatMode = ValueAnimator.REVERSE
start()
}
// Tạo hoạt ảnh cho nhiều thuộc tính cùng lúc
val scaleXAnim = ObjectAnimator.ofFloat(myView, "scaleX", 1f, 1.5f)
val scaleYAnim = ObjectAnimator.ofFloat(myView, "scaleY", 1f, 1.5f)
val alphaAnim = ObjectAnimator.ofFloat(myView, "alpha", 1f, 0.5f)
AnimatorListener — một giao diện để theo dõi các sự kiện hoạt ảnh: onAnimationStart, onAnimationEnd, onAnimationCancel, onAnimationRepeat. Sử dụng AnimatorListenerAdapter để ghi đè chỉ các phương thức cần thiết. Bắt đầu từ Android 12, Animator.AnimationCallback với hỗ trợ coroutine thông qua suspendAnimationFrame đã được thêm vào.
ValueAnimator là lớp cơ sở của Property Animation tạo ra các giá trị được tạo hoạt ảnh từ đầu đến cuối trong một khoảng thời gian nhất định. Không giống như ObjectAnimator, ValueAnimator không gắn với một đối tượng — nó chỉ đơn giản tính toán các giá trị trung gian và thông báo qua một trình lắng nghe. Nhà phát triển quyết định làm gì với các giá trị thu được: đặt thuộc tính View, thay đổi tham số Drawable, cập nhật bản vẽ tùy chỉnh trong onDraw().
import android.animation.ValueAnimator
import android.view.animation.LinearInterpolator
"> ValueAnimator: hoạt ảnh tiến trình tải
val valueAnimator = ValueAnimator.ofFloat(0f, 100f).apply {
duration = 2000L
interpolator = LinearInterpolator()
repeatCount = ValueAnimator.INFINITE
addUpdateListener { animator ->
val progress = animator.animatedValue as Float
progressBar.progress = progress.toInt()
progressText.text = "${progress.toInt()}%"
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationRepeat(animation: Animator) {
Log.d("Anim", "Progress animation restarted")
}
})
start()
}
ValueAnimator.ofInt() — được sử dụng cho các thuộc tính số nguyên (chiều rộng, chiều cao, số bước). ValueAnimator.ofObject() — cho các loại tùy chỉnh với TypeEvaluator. ValueAnimator.ofPropertyValuesHolder() — cho hoạt ảnh song song của nhiều thuộc tính với một bộ đếm thời gian duy nhất, tiết kiệm tài nguyên so với các Animator riêng biệt cho mỗi thuộc tính.
// ValueAnimator.ofObject với PointEvaluator
import android.animation.ValueAnimator
import android.graphics.PointF
import android.animation.PointFEvaluator
val pointAnim = ValueAnimator.ofObject(
PointFEvaluator(),
PointF(0f, 0f),
PointF(300f, 500f)
).apply {
duration = 1000L
addUpdateListener { animator ->
val point = animator.animatedValue as PointF
movingView.x = point.x
movingView.y = point.y
}
start()
}
Hiệu suất ValueAnimator: ở 60 FPS, onAnimationUpdate được gọi mỗi ~16,7 ms. Tránh bên trong trình lắng nghe: cấp phát đối tượng (tạo PointF/RectF một lần), gọi findViewById(), tính toán nặng (I/O tệp, mạng). Để tạo hoạt ảnh cho 20+ phần tử đồng thời, hãy sử dụng PropertyValuesHolder trong một Animator thay vì 20 Animator riêng biệt. Đối với hoạt ảnh thay đổi kích thước, chỉ sử dụng View.setLayoutParams() ở cuối hoạt ảnh (thông qua AnimatorListenerAdapter.onAnimationEnd).
AnimatorSet là một bộ điều phối cho phép chạy nhiều phiên bản Animator (ObjectAnimator, ValueAnimator) theo một thứ tự cụ thể: tuần tự (playSequentially), song song (playTogether), có độ trễ (after, before, with). AnimatorSet hỗ trợ lồng nhau — một AnimatorSet có thể chứa các AnimatorSet khác cho các kịch bản phức tạp.
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
// AnimatorSet: hoạt ảnh tuần tự và song song
val fadeIn = ObjectAnimator.ofFloat(myView, "alpha", 0f, 1f)
val slideUp = ObjectAnimator.ofFloat(myView, "translationY", 100f, 0f)
val bounce = ObjectAnimator.ofFloat(myView, "scaleX", 1f, 1.1f, 1f)
val animatorSet = AnimatorSet().apply {
// Song song: fadeIn + slideUp
play(fadeIn).with(slideUp)
"> Sau: bounce với độ trễ 200ms
play(bounce).after(200L)
duration = 400L
interpolator = FastOutSlowInInterpolator()
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
Log.d("Anim", "Set completed")
}
})
start()
}
AnimatorSet.Builder cung cấp một API mượt mà để xây dựng chuỗi: with() — song song, before() — trước, after() — sau. AnimatorSet hỗ trợ cancel() để dừng tất cả hoạt ảnh trong tập hợp. Đối với các kịch bản phức tạp (25+ bộ hoạt ảnh), hãy sử dụng PropertyValuesHolder — nó hiệu quả hơn vì sử dụng một bộ đếm thời gian dùng chung duy nhất thay vì một bộ riêng cho mỗi Animator.
| Phương thức Builder | Mô tả | Ví dụ |
|---|---|---|
| with(Animator) | Chạy song song với hoạt ảnh hiện tại | play(fadeIn).with(slideUp) |
| before(Animator) | Hoạt ảnh hiện tại trước hoạt ảnh được chỉ định | play(fadeIn).before(bounce) |
| after(Animator) | Hoạt ảnh hiện tại sau hoạt ảnh được chỉ định | play(bounce).after(slideUp) |
| after(Long) | Độ trễ trước hoạt ảnh hiện tại | play(bounce).after(200L) |
Interpolator xác định đường cong tốc độ của hoạt ảnh từ 0 đến 1. Android cung cấp các Interpolator tích hợp: LinearInterpolator (đồng đều), AccelerateDecelerateInterpolator (chậm-nhanh-chậm), FastOutSlowInInterpolator (Material Design), OvershootInterpolator (vượt mục tiêu), BounceInterpolator (có nảy), AnticipateOvershootInterpolator (có kéo lùi trước khi bắt đầu và vượt mục tiêu). Interpolator tùy chỉnh được triển khai thông qua TimeInterpolator.
import android.animation.ObjectAnimator
import android.view.animation.BounceInterpolator
import android.view.animation.OvershootInterpolator
import android.view.animation.AnticipateOvershootInterpolator
// BounceInterpolator — hiệu ứng nảy ở cuối
val bounceAnim = ObjectAnimator.ofFloat(myView, "translationY", 0f, -50f).apply {
duration = 600L
interpolator = BounceInterpolator()
start()
}
// AnticipateOvershoot — kéo lùi, sau đó vượt quá
val anticipateAnim = ObjectAnimator.ofFloat(myView, "scaleX", 1f, 1.3f).apply {
duration = 500L
interpolator = AnticipateOvershootInterpolator(2.0f)
start()
}
TypeEvaluator — một giao diện với phương thức evaluate(fraction, startValue, endValue) tính toán giá trị trung gian giữa đầu và cuối. Tích hợp: ArgbEvaluator (màu int), FloatEvaluator, IntEvaluator, PointFEvaluator, RectEvaluator. Đối với các loại dữ liệu tùy chỉnh (ví dụ: tạo hoạt ảnh cho một lớp tùy chỉnh có ba trường), hãy triển khai TypeEvaluator của riêng bạn. Để tạo hoạt ảnh cho nhiều View với cùng cài đặt, hãy sử dụng PropertyValuesHolder — nó chấp nhận một mảng tên thuộc tính và các giá trị tương ứng.
// TypeEvaluator tùy chỉnh cho lớp ProgressState
data class ProgressState(val progress: Float, val color: Int)
class ProgressEvaluator : TypeEvaluator<ProgressState> {
override fun evaluate(
fraction: Float,
startValue: ProgressState,
endValue: ProgressState
): ProgressState {
val progress = startValue.progress + fraction * (endValue.progress - startValue.progress)
val color = ArgbEvaluator().evaluate(
fraction, startValue.color, endValue.color
) as Int
return ProgressState(progress, color)
}
}
"> PropertyValuesHolder — hiệu suất tối đa
val pvh1 = PropertyValuesHolder.ofFloat("scaleX", 1f, 1.5f)
val pvh2 = PropertyValuesHolder.ofFloat("scaleY", 1f, 1.5f)
val pvhAnim = ObjectAnimator.ofPropertyValuesHolder(myView, pvh1, pvh2).apply {
duration = 300L
start()
}
Các câu hỏi thường gặp
View Animation (Tween Animation) chỉ thay đổi biểu diễn trực quan của View thông qua biến đổi ma trận, mà không ảnh hưởng đến các thuộc tính thực của đối tượng. Sau hoạt ảnh translationX trong View Animation, phần tử bị dịch chuyển trực quan, nhưng các cú nhấp chuột vẫn được xử lý tại vị trí ban đầu. Property Animation (ObjectAnimator) thay đổi thuộc tính thực — sau hoạt ảnh, phần tử ở vị trí mới về mặt vật lý.
Gọi animator.cancel() — hoạt ảnh dừng ngay lập tức, giá trị cuối cùng không được áp dụng. Gọi animator.end() — hoạt ảnh chuyển sang trạng thái cuối và dừng lại. Đối với AnimatorSet — set.cancel() dừng tất cả hoạt ảnh trong tập hợp. Animator.pause() và resume() có sẵn trên API 19+ để tạm dừng.
Material Design khuyến nghị: FastOutSlowInInterpolator — cho hoạt ảnh xuất hiện và biến mất của phần tử, LinearInterpolator — cho chỉ báo tải, OvershootInterpolator — cho hiệu ứng nhấn mạnh (nút), BounceInterpolator — cho pull-to-refresh và các phần tử lò xo. Đối với hoạt ảnh xuất hiện từ bên ngoài màn hình, hãy sử dụng AccelerateDecelerateInterpolator.
Kiểm tra: (1) đối tượng có setter công khai cho thuộc tính được chỉ định (setPropertyName()), (2) thuộc tính được viết bằng camelCase với chữ cái thường sau “set”, (3) setter chấp nhận cùng loại bạn đang truyền (ofFloat → setPropertyName(float)). Đối với View, tất cả các thuộc tính tiêu chuẩn (alpha, translationX, rotation, v.v.) đều có setter.
ObjectAnimator — khi bạn đang tạo hoạt ảnh cho một thuộc tính View tiêu chuẩn duy nhất (alpha, translationX, rotation). ValueAnimator — khi bạn cần cập nhật nhiều thuộc tính từ một trình lắng nghe, tạo hoạt ảnh cho các thuộc tính không phải View (tiến trình, bản vẽ tùy chỉnh), hoặc khi đối tượng không có setter cho thuộc tính cần thiết. ValueAnimator linh hoạt hơn nhưng yêu cầu nhiều mã hơn cho các cập nhật thủ công.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm