안드로이드

Object Animator 사용법!!

강한 맷돼지 2022. 2. 16. 11:40

애니메이션의 대략적인 사용처에 대한 정리글을 작성했는데 거기서 가장 1빠로 내가 현재 당장 사용중인 

Object Animator에 대해 사용법을 정리해보려한다.

 

Object Animator의 용도와 특징은 앞전에 글에서 정리했었다 간단하게만 짚고 넘어가보자면

안드로이드의 Animator객채내에 속해있으며 원래는 아주 간단한것은 View property Animator를 사용하고

또 복잡한거는 Value animator를 사용해야하지만

 

사실상 간단한거도 그냥 Object Animator 사용해도되고 왠만한 내가 원하는 복잡한것도 다 Object Animator로 처리가 가능하다 사실 진짜 뿌슝빠슝 애니메이션은 로티로 처리해야지 개발자한테 하라고하면 그건좀 인간적으로 너무하다고 생각한다.(대부분의 개발자들이 그만큼의 미적감각도없고)

 

그래서 결론적으로 그냥 왠만한거 다 Object Animator로 사용하자

 

서론이 길었고 이제 사용법을 봐보자

 

 


사용법을 나눠보자면 Object Animator는 xml로 정리해서 사용해도 되고 직접 코드에서 객체생성하고 바로바로 속성을 박아 넣어서 사용할수도 있다(스파크에서 이렇게 사용). 

두 방법다 사용해본 결과 왠만해서 (아주 간단한 작업을 하는것이 아닌이상) xml에 정리해서 사용하는것을 추천한다.

왜냐하면 애니메이션을 바로바로 코드에서 만든다면 util 로 빼서 사용하거나 아니면 직접 바로 사용처(액티비티,프래그먼트) 에서 코드를 주루룩 나열하게 되는데 정리되는게 어지러울 뿐더러 막 여러개의 애니메이션을 순차적으로 나열하고 그걸 막 뭐 순서대로 리스너에 달아주고 하다보면 진짜 정신없고 어지럽고 코드가 더러워진다.

 

 

fun lostFocusInSetPurpose(
    textviewOne: TextView,
    textviewTwo: TextView,
    editTextOne: EditText,
    editTextTwo: EditText,
    constraintLayout: ConstraintLayout
) {
    editTextOne.isClickable = false
    editTextTwo.isClickable = false
    ObjectAnimator.ofFloat(constraintLayout, "translationY", 0f).apply {
        start()
        addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                ObjectAnimator.ofFloat(textviewTwo, View.ALPHA, 0f, 1f).apply {
                    duration = 500
                    start()
                }
                ObjectAnimator.ofFloat(textviewOne, View.ALPHA, 0f, 1f).apply {
                    addListener(object : AnimatorListenerAdapter() {
                        override fun onAnimationStart(animation: Animator?) {
                            super.onAnimationStart(animation)
                            textviewOne.visibility = View.VISIBLE
                            textviewTwo.visibility = View.VISIBLE
                            editTextOne.isClickable = true
                            editTextTwo.isClickable = true
                        }
                    })
                    duration = 500
                    start()
                }
            }
        })
    }
}

더러워진 애니메이션의 예시 -> 심지어 이게 그냥 간단하게 순차적으로 움직인다 이정도의 간단한 애니메이션이다 

(물론 이때 Animator set을 몰라서 안써서 이렇게 된거긴함)

 

 

xml을 이용한 방법을 알면 다양하게 활용해서 적용할수있으니 우선 xml을 이용한 방법부터보자

 


1.XML을 사용한 Object Animator구현법

xml 파일의 위치는 

animator라는 디렉토리를 생성하고 그안에 만들어준다.

이렇게 animator 디렉토리 생성하고 그안에 xml파일을 만들어주는것으로 시작한다.

 

 

이제 속성들을 Object Animator에서 사용되는 속성을 살펴보도록 하겠다

 


 

ObjectAnimator attribute

Object Animator에서 사용되는 속성은 다음과같다

(이거 뭐 다른 애니메이터에도 대충 여기저기서 통용되니까 주의깊게보자)

 

<set
  android:ordering=["together" | "sequentially"]>

    <objectAnimator
        android:propertyName="string"
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["restart" | "reverse"]
        android:valueType=["intType" | "floatType"]
        android:interpolator="@android:interpolator/accelerate_decelerate"/>

</set>

속성들을 설명하자면 이렇다

  • ordering
    • together : set에 있는 애니메이션을 동시에 재생 (default)                                                                    -> Together로 해놓고 각각을 startOffset을 통해서 시작 순간 조정가능
    • sequentially : set에 있는 애니메이션을 순차 재생
  • propertyName : 객체에서 animate 시킬 속성을 지정합니다
    • alpha(fade in,fade out 할때 쓰는 스르륵 생기기 사라지기 투명도)
    • rotationX(위아래로 회전 회전문을 옆으로 돌려놓음),rotationY(회전문처럼 돌음) rotation(시계방향 반시계방향) 
    • translationX(x축이동), translationY(Y축으로 이동)
    • backgroundColor(배경색 변화) 
    • textColor(텍스트 색상 변화) 
    • scaleX(x방향 늘리기) scaleY(y방향 늘리기) pivot은 직접 뷰에다가 설정해줘야함 (예시 코드)
    • xml 내에서는 객체 지정이 불가능 하므로, 코드 내에서 ObjectAnimator.setTarge()으로 해당 속성을 가진 객체를 지정해줘야 합니다. ->즉 이거 xml하나당 한가지 view의 애니메이션을 지정할수있는 것임 뭐 순차적인거 뭐그런건 할수있지만 동시에 여러뷰를 움직이려면 Animatior Set을 이용해야함 ->( 기본적으로 XML상에서도 SET안에 들어있기때문에 한가지 뷰에대해서는 여러가지 애니메이션을 순차적으로든 동시에든 실행시킬수있지만 여러뷰를 움직일때는 코트에서 Animator set을 사용해서 어떤순서로 어떤방식으로 실행시킬지 지정해줘야한다.)
  • duration : 애니메이션 실행 시간 ms단위
  • valueFrom : propertyName에서 지정한 속성값의 시작값을 지정합니다. 
  • valueTo : propertyName에서 지정한 속성값의 종료값을 지정합니다.
  • valueTpye : animate될 값의 타입을 지정합니다. color에 대한 애니메이션의 경우 colorType을 사용합니다. (위치이동같은거 float 하면되고 200지정하면 200dp움직임
  • startOffset : 애니메이션의 시작 시간을 지정합니다. (1000 → start() 후 1초 후 실행)
  • repeatMode : 반복 모드를 지정합니다.
    • restart : 처음 상태로 다시 돌아가서 반복 ( 1 → 2 → 3 → 1 → 2 → 3 )
    • reverse : 종료된 상태에서 반복 ( 1 → 2 → 3 → 2 → 1 )
  • repeatCount : 반복 횟수를 지정합니다. -1(infinite)로 지정 시, 무한 반복합니다.
  • interpolator : 애니메이션 진행 속도에 대한 옵션

    -linear_interpolator : 등속 (일정한 속도로 진행)

    -accelerate_interpolator : 가속 (느리다가 빨라짐)

    -anticipate_interpolator : 기대? (시작시 조금 당겼다가 시작)

    -bounce_interpolator : 바운스 (마지막에 약간 통통 튐)

    -cycle_interpolator : 순환 (진행이 끝나면 다시 역재생)

    -decelerate_interpolator : 감속 (빠르다가 느려짐)

    -overshoot_interpolator : 초과 (약간 더 진행되었다가 돌아옴)

여기서 필요한것들만 사용하면 된다 결론적으로 이거는 한가지 뷰에대해서 애니메이션을 표현할수있는법을 배웠다.

그리고 set안에는 Object Animator를 여러개를 배치해서 여러가지 애니메이션을 한뷰대상으로 표현할수있다.

 

 


 

 

동시에 여러가지 애니메이션을 간결한 표현으로 넣고싶다면?

-> Set에 여러개의 ObjectAnimator를 정의할 수도 있지만, propertyValuesHolder 를 사용해서 구현할 수 있다.

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="5000"
    android:interpolator="@android:interpolator/accelerate_quad">
    <propertyValuesHolder
        android:propertyName="translationY"
        android:valueFrom="0dp"
        android:valueTo="-200dp"
        android:valueType="floatType"/>
    <propertyValuesHolder
        android:propertyName="backgroundColor"
        android:valueFrom="@color/black"
        android:valueTo="@color/purple_200"
        android:valueType="colorType"/>
</objectAnimator>

이런식으로 propertyValuesHolder를 사용하면, 하나의 ObjectAnimator 루트에서 여러가지 속성들에 대한 애니메이션을 정의할 수 있습니다.

 

이러면 하나의 Object Animator 즉 하나의 뷰의 여러가지 애니메이션 표현을 간결하게 할수있다(동시에 여러애니메이션을 넣을때 사용( ex)색깔이바뀌면서 이동) 그리고 이것도 set안에 넣어서 여러가지 다른 Object Animator와 같이 사용할수 있다.

 

이렇게 xml을 통해서 하나의 뷰에 적용할수있는 애니메이션들을 정의하는 방법을 알아보았다 이제 

이것을 하나의 뷰에  적용시키는 법과 여러가지 뷰에 한번에 적용시키는 법을 알아볼것이다.

 


 

애니메이션 적용시키는법

ObjectAnimator객체생성여기서는 애니메이션 다는 방법에 따라서 2가지를 제시한다.

 

1.내가 쓰는방법

val animationColorChange =
                AnimatorInflater.loadAnimator(this, R.animator.object_ani_test5).apply {
                    addListener(object : AnimatorListenerAdapter(){
                        override fun onAnimationStart(animation: Animator?) {
                            super.onAnimationStart(animation)
                            Toast.makeText(this@MainActivity,"시작확인",Toast.LENGTH_SHORT).show()
                        }

                        override fun onAnimationEnd(animation: Animator?) {
                            super.onAnimationEnd(animation)
                            Toast.makeText(this@MainActivity,"끝확인",Toast.LENGTH_SHORT).show()
                        }
                    })

                    setTarget(binding.tvAnimationText)
                    start()
                }

add Listener에 AnimatorListenerAdapter 넘기기  -> 이방식으로 사용한다면 모든 시점을 오버라이딩 해줄필요없이 내가원하는 시점만 해줄수있다. -> 내가 원하는 시점만 오버라이딩 해주면됨

그리고 객체를 만들때 여기서 duration,interpolator 을 지정해줄수있지만 이런것도 걍 xml에서 다 정리하자

 

그리고 객체에 setTarget()으로 어느 뷰를 움직일껀지 타겟을 정해주고

start()를 통해서 시작한다

 

그리고 stop() 을 통해서 애니메이션을 중단시킬수있다.

 

cancel로 애니메이션 자체를 취소시킬수있다.

 

 

2.두번째 방법 

val intervalXAnimationColorChange = AnimatorInflater.loadAnimator( this, R.animator.object_ani_interval_color_change ).apply {
            duration = 1000
            interpolator = AccelerateDecelerateInterpolator()
            addListener( object : Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator?) {
                    Toast.makeText( this@ObjectAnimatorActivity, "애니메이션 시작", Toast.LENGTH_SHORT ).show()
                }

                override fun onAnimationEnd(animation: Animator?) {
                    Toast.makeText( this@ObjectAnimatorActivity, "애니메이션 종료", Toast.LENGTH_SHORT ).show()
                }

                override fun onAnimationCancel(animation: Animator?) {
                    Toast.makeText( this@ObjectAnimatorActivity, "애니메이션 취소", Toast.LENGTH_SHORT ).show()
                }

                override fun onAnimationRepeat(animation: Animator?) {
                    Toast.makeText( this@ObjectAnimatorActivity, "애니메이션 시작", Toast.LENGTH_SHORT ).show()
                }
            })
        }

참고한 블로그에서 나온방법인데 저렇게 addListener에 Aniamtor.AnimatorListener를 전달할수있는데 이러한 경우에는

모든 시점을 다 오버라이딩 해줘야하는 불편함이있어 안쓸거같다.

 

3.그리고 이방법에 대한 대안으로 간단하게 사용하는 방법으로 세번째는

fun initAnimation() {
    binding.btnAnimationStart.setOnClickListener {
        val animationColorChange =
            AnimatorInflater.loadAnimator(this, R.animator.object_ani_test).apply {
                doOnStart { Toast.makeText(this@MainActivity, "시작", Toast.LENGTH_SHORT).show() }
                doOnEnd { Toast.makeText(this@MainActivity, "끝", Toast.LENGTH_SHORT).show() }
                setTarget(binding.tvAnimationText)
                start()
            }

doOn시점() 메서드를 이용해서 간단하게 리스너를 지정해줄수도있다

doOn메서드 종류

 

이런식으로 객체를 만들어서 애니메이션을 뷰에 지정해서 사용할수있다.

 

이시점이 되면 의문이들것이다.

 

근데 이거 다 한가지뷰를 가지고 움직이는 거자나!! 그럼 여러가지 뷰를 동시 움직이거나 동시가 아니더라도 순차적으로 움직이려면 애니메이션 리스너에 붙여서 끝날때 호출하는식으로 무슨 콜백지옥마냥 불러줘야하나?

 -> 이걸해결하기위해 Animator set이 있다.!!

 


Animator Set

Animator Set은 여러 뷰를 동시에 혹은 순자적으로 움직이기위해 애니메이션들을 세트로 묶는 것이다.

한마디로 여태까지는 계속 하나의 뷰를 움직이는거였는데 동시에 여러뷰를 움직이고 싶거나 순차적으로 움직이도록 만들고싶으면 이걸 쓰면 된다.

기존에xml 에서도 set이 있긴하지만 그건 하나의 뷰에다만 적용할수있어 여러뷰에 적용하고자하면 코드에서 Animator Set를 만들어주고 지정해줘야한다.

 

 

사용방법

1.일단 원하는 애니메이션들을 실행시켜줄수있는 Object Animator를 다 만들어준다

val animationColorChange =
    AnimatorInflater.loadAnimator(this, R.animator.object_ani_test).apply {
        doOnStart { Toast.makeText(this@MainActivity, "시작", Toast.LENGTH_SHORT).show() }
        doOnEnd { Toast.makeText(this@MainActivity, "끝", Toast.LENGTH_SHORT).show() }
        setTarget(binding.tvAnimationText)
    }

val animationButtonChange =
    AnimatorInflater.loadAnimator(this, R.animator.object_ani_test4).apply {
        setTarget(binding.btnAnimationStart)
    }


val animationRotate =
    AnimatorInflater.loadAnimator(this,R.animator.object_ani_test).apply {
        setTarget(binding.btnAnimationStart)
    }

예시로 3가지 애니메이션을 만들어줬다

 

그리고 Animator Set을 Animator Set으로 또 묶을수있는걸 보여주기위해서 일단 두개만 묶었다.

val firstAnimationSet = AnimatorSet().apply {
    play(animationColorChange).after(2000).before(animationButtonChange)
}

 

일단 Animator Set객체를 만들어주고 거기에 여러가지 속성들을  넣어서 어떤식으로 실행할지 설정해줄수있다.

 

이예시에서는 아까만든 두가지 애니메이션을 순서에 맞춰서 플레이하도록 만들어져있다

 

위쪽에서 설명한 Object Animator와 거의 비슷하기때문에 공식문서의 속성들을 본다면 적절히 사용할 수 있을것이다. 

링크를 통해 속성들을 살펴보고 사용하자

 

https://developer.android.com/reference/android/animation/AnimatorSet?hl=ko 

 

AnimatorSet  |  Android Developers

android.net.wifi.hotspot2.omadm

developer.android.com

이중 많이 쓰는 몇가지를 살펴보자면

당연히 play,pause,resume,start는 되고

 

playSequentially,playTogether를 통해서 같이 혹은 순차적으로 실행시킬수있고

 val set = AnimatorSet()
        set.playTogether(mover, rotator)

이런식으로 사용하면된다.

 

reverse로 거꾸로 돌아가게 설정가능하며

Interpolator,startDelay,duration도 set마다 설정 가능하다.

 

그리고 playSequentially,playTogether 말고도 

순서에 관련해서 실행시키는 방법은

 

val bouncer = AnimatorSet().apply {
        play(bounceAnim).before(squashAnim1)
        play(squashAnim1).with(squashAnim2)
        play(squashAnim1).with(stretchAnim1)
        play(squashAnim1).with(stretchAnim2)
        play(bounceBackAnim).after(stretchAnim2)
    }

이런식으로 

before,after,with를 통해서 먼저실행할지 나중에 실행할지 같이 실행할지 애니메이션마다 구분해줄수있다

근데 이거 before,after 관계가 엄청 헷갈리니 주의하자 그리고 after같은경우에는 시간도 지정해줄수 있어서 몇초후에 다음꺼 실행해라 이런식으로 지정해줄수도있다.

저기 long이 딸려있는 after 쓴다면 중간에 끼워서 딜레이를 줄수있을것이다.

val firstAnimationSet = AnimatorSet().apply {
    play(animationColorChange).after(2000).before(animationButtonChange)
}

이런식으로 사용하면된다.

 

 

그리고 Animator set 안에 Animator set을 또 하위 자식으로 둘수도 있다.

 

val firstAnimationSet = AnimatorSet().apply {
    play(animationColorChange).after(2000).before(animationButtonChange)
}

AnimatorSet().apply{
    play(firstAnimationSet).with(animationRotate)
    start()
}

이런식으로 사용할수도있다.

 

그래서 적절히 원하는대로 조합해서 순서에 맞도록 여러가지 애니메이션을 실행시켜주면 될것이다.


최종적으로 간단히 사용할수있는 코드에서 바로 object Animator 객체를 만들어서 사용하는법을 보자

본인이 그냥 xml같은거 만들기 싫고 바로바로 사용하고싶다면?

아니면 아주 간단한거라 그냥 코드에서 바로 구현하고싶다면 이방식을 이용하면 더 편리할것이다.

 

ObjectAnimator.ofFloat(buttonMain, View.ROTATION, -45f, 0f).apply {
                duration = 300
                start()
            }

이런식으로 하는건데  ofFloat는 위에서 설명한 valueType 와 같은것이다 만약 color을 지정한다면 ofArgb를 지정해주면된다. 그리고 들어가는 매개변수로는 첫번째는 타겟뷰, 두번쨰로 어떤 애니매이션을 넣을것인지, 세번쨰는 얼만큼움직일것인지 이다 -> 매개변수를 4개를 지정해주면 3번째는 시작점 4번째는 종료지점이다.

 

그리고 duration같은 속성들을 apply 안에다 죄다 지정해주면 사용이 가능하다. 이런식으로도 사용가능하니 적절히 필요할때 사용하자