반응형
Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

코딩하기 좋은날

Android 애니메이션 구현하기(feat. ValueAnimator) 본문

Android

Android 애니메이션 구현하기(feat. ValueAnimator)

huiung 2021. 8. 16. 01:10
반응형

안드로이드에서 애니메이션을 구현하는 방법은 여러가지가 있습니다.

 

ObjectAnimator, ViewPropertyAnimator, ValueAnimator, OpenGL 등등을 이용 할 수 있는데

 

개인적으로 저는 ValueAnimator를 자주 사용합니다. ValueAnimator만 사용하더라도 ObjectAnimator나 ViewProperyAnimator로 할 수 있는 동작을 동일하게 할 수 있고, ValueAnimator는 다양한 뷰에 대해서 동시에 처리가 가능하기 때문에 자주 사용하는데요. 예시로 들 애니메이션은 아래의 영상에 나오는 카운트 다운입니다.

 

화면 상단에서 애니메이션이 진행됨에 따라 화면의 중앙까지 숫자가 내려오며 알파값이 점점 증가하는 애니메이션 입니다.

 

 

아래는 구현 코드입니다.

ValueAnimator.ofFloat(0.3f, 1f).apply {
            duration = 1000
            interpolator = DecelerateInterpolator()
            addUpdateListener {
                val offset = it.animatedValue as Float
                binding.countText.translationY = (offset - 1) * heightPixels
                binding.countText.alpha = offset
            }

            addListener(  
                onRepeat = {
                   cnt--
                   binding.countText.text = cnt.toString()
                },
                onEnd = {
                    
                }
            )
            repeatCount = 2
            start()
        }

 

ofFloat()의 인자에 애니메이션이 진행되는 동안 들어올 offset의 범위를 지정 합니다. 범위 지정 이후 여러가지 옵션을 필요에 따라 추가 해주면 되는데요.

 

duration: 애니메이션이 지속되는 시간입니다.

 

interpolator: offset이 어떻게 변화할지를 지정해 줄 수 있습니다.

일반적으로 LinearInterpolator(), DecelerateInterpolator(), AccelerateInterpolator() 가 있는데요. 

Linear - 일정한 속도로 offset이 변합니다.

Decelerate - 초반에 빠르게 변하고 점점 느리게 변합니다.

Accelerate - 초반에 느리게 변하고 점점 빠르게 변합니다.

 

이 3개의 클래스는 생성할때 factor값을 주어서 그래프의 기울기를 조절 할 수 있습니다.

이외에 따로 필요하다면 본인의 입맛에 따라 Interpolator를 만들어서 사용 할 수 있습니다.

저는 보통 아래의 사이트를 자주 이용합니다.

 

http://inloop.github.io/interpolator/

 

Interpolator

Interpolator Realtime interpolation equation WYSIWYG editor Use JavaScript syntax to create complex equations. Result of equation is evaluated as y (depends on type of visualization: movement = vertical position from X to Y point; scaling = scale X and Y s

inloop.github.io

 

addUpdateListener: 애니메이션이 진행되면서 UpdateListener가 반복적으로 실행 됩니다. 따라서 offset에 따라 어떤 동작을 할 지 정의해주면 됩니다.

 

addListener: 애니메이션이 시작될때, 반복될때, 끝났을때, 취소 됐을때 취할 동작을 람다식으로 전달 할 수 있습니다. 해당 함수는 아래처럼 inline 함수로 정의되어 있습니다.

 

inline fun Animator.addListener(
    crossinline onEnd: (animator: Animator) -> Unit = {},
    crossinline onStart: (animator: Animator) -> Unit = {},
    crossinline onCancel: (animator: Animator) -> Unit = {},
    crossinline onRepeat: (animator: Animator) -> Unit = {}
): Animator.AnimatorListener {
    val listener = object : Animator.AnimatorListener {
        override fun onAnimationRepeat(animator: Animator) = onRepeat(animator)
        override fun onAnimationEnd(animator: Animator) = onEnd(animator)
        override fun onAnimationCancel(animator: Animator) = onCancel(animator)
        override fun onAnimationStart(animator: Animator) = onStart(animator)
    }
    addListener(listener)
    return listener
}

 

repeatCount: 해당 애니메이션을 몇번 반복할지 설정 할 수 있습니다.

 

마지막으로 start() 호출을 통해 애니메이션을 실행 시킵니다.

 

보통은 이정도만 사용하여도 원하는 애니메이션이 구현이 될 것입니다.

 

주의사항

애니메이터를 다룰 때 주의할 점이 몇가지 있는데, 일반적으로 화면의 끊김이 없으려면 1프레임을 그리는 시간이 16ms 이내에 이루어져야 합니다. (60fps 기준) 따라서 UpdateListenr에서 시간이 오래 걸리는 동작을 해서는 안됩니다. 

 

예를들면 UpdateListener에서 View의 크기를 변경한다던지 하는 행동을 해서는 안됩니다. (requestLayout의 호출을 유발 할 수 있는..) View크기 변경이 필요한 경우는 Scale 함수를 이용하여 inValidate()의 호출만 유도하여야 합니다. 마찬가지로 위치를 변경한다던가 하는 동작도 위의 예처럼 translation..을 이용하여 처리해주면 됩니다. 

 

Animaotr를 사용할때 또 한가지 주의할점은 안드로이드의 개발자 옵션에서 애니메이션 효과를 사용 안함으로 지정 한경우 ValueAnimator 기준 마지막 값만 들어오며 바로 onEnd 호출이 일어납니다. 따라서 적절하게 끝에 바로 도달했을 때 마지막 상태처리를 잘 해주어야 합니다.

 

ValueAnimator의 단점으로는 연속된 애니메이션 처리가 조금 까다로운데요. 예를들어 한 뷰의 애니메이션이 끝난 다음 다른 뷰의 애니메이션을 진행 시키고 싶은 경우가 있을 수 있습니다. 이 경우는 AnimationSet 을 사용하면 처리가 쉽다는 것 같습니다. 물론 ValueAnimator에서도 내부에서 offset값을 나눠서 처리는 가능하지만 계산이 조금 귀찮아 질 수 있습니다.

 

추가적으로, ValueAnimator의 실행은 해당 액티비티의 라이프사이클과는 관계가 없습니다. 따라서 animator가 실행된 Activity나 Fragment가 Destroy되도 애니메이션은 끝까지 진행을 하게 됩니다. 가령 위의 예처럼 binding을 non nullable 타입으로 사용하고 있는 경우 애니메이션이 끝나기 전에 해당 화면을 destroy시켜 버리면 null pointer exception이 발생할 수 있습니다. 따라서 이 경우에는 binding에 대한 null check를 해준다던지, onStop 단계에서 animator의 cancel()함수를 이용하여 동작을 취소해주는 처리가 필요합니다.

 

이외에 만약 3D애니메이션을 한다던지.. 복잡한 렌더링 애니메이션이 필요한 경우에는 OpenGL을 이용하여야 합니다.

애니메이션쪽에 관심이 꽤 생겨서 OpenGL도 천천히 공부를 해보려고 합니다 ㅎㅎ..

 

 

반응형