Android에서 애니메이션을 어떻게 구현하나요?
애니메이션은 부드러운 전환을 통해 사용자 경험을 향상시키고, 변화에 주목하게 하며, 시각적 피드백을 제공합니다. Android는 단순한 속성 변경부터 정교한 레이아웃 애니메이션까지 다양한 구현 방법을 제공합니다.
View Property Animation
API 레벨 11에서 도입된 View Property Animation은 alpha, translationX, translationY, rotation, scaleX 등 View 객체의 속성을 애니메이션화합니다. 단순한 변형에 이상적인 방법입니다.
val view: View = findViewById(R.id.my_view)
view.animate()
.alpha(0.5f)
.translationX(100f)
.setDuration(500)
.start()
ObjectAnimator
ObjectAnimator는 View뿐만 아니라 setter 메서드가 있는 모든 객체의 속성을 애니메이션화할 수 있어, 커스텀 속성 애니메이션에 더 큰 유연성을 제공합니다.
val animator = ObjectAnimator.ofFloat(view, "translationY", 0f, 300f)
animator.duration = 500
animator.start()
AnimatorSet
AnimatorSet은 여러 애니메이션을 순차적으로 또는 동시에 실행할 수 있게 결합하여, 복잡한 애니메이션 조합에 적합합니다.
val fadeAnimator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)
val moveAnimator = ObjectAnimator.ofFloat(view, "translationX", 0f, 200f)
val animatorSet = AnimatorSet()
animatorSet.playSequentially(fadeAnimator, moveAnimator)
animatorSet.duration = 1000
animatorSet.start()
ValueAnimator
ValueAnimator는 임의의 값 사이를 애니메이션화하는 범용적인 방법으로, 높은 커스터마이징 유연성을 제공합니다. 인터폴레이터로 진행 방식을 제어할 수 있어 너비, 높이, 알파 등 다양한 속성에 정밀하고 동적인 애니메이션을 구현할 수 있습니다.
val valueAnimator = ValueAnimator.ofInt(0, 100)
valueAnimator.duration = 500
valueAnimator.addUpdateListener { animation ->
val animatedValue = animation.animatedValue as Int
binding.progressbar.updateLayoutParams {
width = ((screenSize / 100) * animatedValue).toInt()
}
}
valueAnimator.start()
XML 기반 View 애니메이션
XML 기반 애니메이션은 리소스 파일에 애니메이션을 정의하여 재사용성을 높입니다. 위치, 크기, 회전, 투명도 변환에 활용할 수 있습니다.
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="-100%"
android:toXDelta="0%"
android:duration="500" />
val animation = AnimationUtils.loadAnimation(this, R.anim.slide_in)
view.startAnimation(animation)
MotionLayout
MotionLayout은 ConstraintLayout 위에 구축된 도구로, XML을 사용하여 상태 간 전환과 모션을 정의할 수 있습니다. 상태 전환과 복잡한 모션을 정밀하게 제어할 때 적합합니다.
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@id/end"
app:duration="500">
<OnSwipe
app:touchAnchorId="@id/box"
app:touchAnchorSide="top"
app:dragDirection="dragDown" />
</Transition>
</MotionScene>
<androidx.constraintlayout.motion.widget.MotionLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/motion_scene">
<View
android:id="@+id/box"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@color/blue" />
</androidx.constraintlayout.motion.widget.MotionLayout>
Drawable 애니메이션
Drawable 애니메이션은 AnimationDrawable을 사용한 프레임 단위 전환으로, 로딩 스피너와 같은 단순한 애니메이션을 만들 때 적합합니다.
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/frame1" android:duration="100" />
<item android:drawable="@drawable/frame2" android:duration="100" />
</animation-list>
val animationDrawable = imageView.background as AnimationDrawable
animationDrawable.start()
물리 기반 애니메이션
물리 기반 애니메이션은 실제 물리 역학을 시뮬레이션합니다. Android는 자연스럽고 동적인 모션 효과를 위한 SpringAnimation과 FlingAnimation API를 제공합니다.
val springAnimation = SpringAnimation(view, DynamicAnimation.TRANSLATION_Y, 0f)
springAnimation.spring.stiffness = SpringForce.STIFFNESS_LOW
springAnimation.spring.dampingRatio = SpringForce.DAMPING_RATIO_HIGH_BOUNCY
springAnimation.start()
요약
- Q) 버튼 클릭 시 확장·축소되는 부드러운 애니메이션을 효율적으로 구현하려면 어떻게 해야 하나요?
버튼의 확장·축소 애니메이션은 ObjectAnimator와 AnimatorSet을 활용하여 구현할 수 있습니다.
fun animateButton(button: View) {
val scaleX = ObjectAnimator.ofFloat(button, "scaleX", 1f, 1.2f, 1f)
val scaleY = ObjectAnimator.ofFloat(button, "scaleY", 1f, 1.2f, 1f)
val animatorSet = AnimatorSet()
animatorSet.playTogether(scaleX, scaleY)
animatorSet.duration = 300
animatorSet.interpolator = OvershootInterpolator()
animatorSet.start()
}
성능을 위해 다음 사항을 고려합니다.
hardware layer를 일시적으로 활성화하여 GPU 렌더링을 활용합니다.
레이아웃을 무효화하는 속성(width, height)보다 scaleX, scaleY처럼 렌더링 속성만 변경하는 방식을 선호합니다.
애니메이션 완료 후 레이어 타입을 원래대로 복원합니다.
- Q) MotionLayout을 기존 View 애니메이션 대신 사용해야 하는 경우는 언제인가요?
MotionLayout이 적합한 경우:
두 가지 이상의 레이아웃 상태 간 전환이 필요한 경우
드래그, 스와이프 등 사용자 제스처와 연동되는 애니메이션
여러 뷰가 동시에 다른 방식으로 움직이는 복잡한 다중 뷰 애니메이션
키 프레임을 사용해 중간 상태를 세밀하게 제어해야 하는 경우
반대로, 단순한 페이드나 이동처럼 단일 뷰의 간단한 변환은 ObjectAnimator나 ViewPropertyAnimator가 더 간결하고 빠릅니다. MotionLayout은 강력하지만 설정이 복잡하므로, 단순한 애니메이션에는 과한 선택일 수 있습니다.