본문 바로가기
개발/안드로이드

Android property animation

by darksilber 2013. 6. 3.
반응형

출처 - http://blog.naver.com/PostView.nhn?blogId=xicnt&logNo=10152628778

 

기존 Animation 방식에 비해서 View가 아닌 Object 등 모든 것들에 Animation을 적용 할 수 있음.

 

Animating with ValueAnimator

  • TimeInterpolator
  • TypeEvaluator
  • duration
  • startPropertyValue
  • endPropertyValue

 

ex>

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);

animation.setDuration(1000);

animation.start()

 

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);

animation.setDuration(1000);

animation.start();

 

ValueAnimator가 Object에 직접적인 실제 변화를 주지는 않는다. 이를 위해서는 AnimationListener를 달아서 getAnimatedValue를 통해 계산 된 값을 얻어 변화를 주어야 한다.

 

Animating with ObjectAnimator extends ValueAnimator

timing engine과 ValueAnimator의 값 계산, 타겟 오브젝트의 속성명으로 animate.

 

ex>

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);

anim.setDuration(1000);

anim.start();

 

ObjectAnimator를 사용하기 위해서는 아래 사항을 준수.

  • property Name에 대해서 setter method가 camel 표기 법을 따라서 존재해야 함. (foo 가 setter라면 setFoo() )

    • option 1 : setter 추가
    • option 2 : Wrapper class를 만들어서 setter 추가.
    • option 3 : 대신에 ValueAnimator를 사용.
  • 만약 ObjectAnimator factory method에서 값을 1개만 주면 이는 end value로 간주 된다. 그러므로 ObjectAnimator가 start value를 얻기 위한 getter method가 필요하다. getFoo()
  • property의 type에 따라서 getter, setter는 같은 타입을 갖도록 해야 함.
  • animating하는 property나 object에 따라서 해당 값을 적용 할 View invalidate 할 필요가 있음.

    • 이것은 onAnimationUpdate() callback을 이용하여 처리.
    • 만약 View의 RGB값 변경 같은 경우는 onAnimaionUpdate에서 invalidation을 따로 해줄 필요가 없어짐. 왜냐하면 이미 그런 Drawable에는 setAlpha, getAlpha method가 이미 준비되어 있고, property는 alpha와 같이 이미 해당 이름으로 명시했을 것이기 때문이다.

 



Multiple Animations with AnimatorSet

AnimatorSet을 통해 Animation은 여러가지를 동시, 순차적 혹은 지연 시간을 주고 진행 시킬 수 있다.

또한 AnimatorSet은 중복 될 수 있다.

 

example>

AnimatorSet bouncer = new AnimatorSet();
bouncer
.play(bounceAnim).before(squashAnim1);
bouncer
.play(squashAnim1).with(squashAnim2);
bouncer
.play(squashAnim1).with(stretchAnim1);
bouncer
.play(squashAnim1).with(stretchAnim2);
bouncer
.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim
.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet
.play(bouncer).before(fadeAnim);
animatorSet
.start();

 

Animation Listeners

Animator.AnimatorListener

  • onAnimationStart()

    • 애니메이션 시작 될 때
  • onAnimationEnd()

    • 애니메이션 끝 날 때
  • onAnimationRepeat()

    • 애니메이션이 반복 될 때
  • onAnimationCancel()

    • 애니메이션이 취소 될 때. onAnimationEnd() 도 불림.

ValueAnimator.AnimatorUpdateListener

  • onAnimationUpdate()

    • 애니메이션의 매 프레임마다 불림. 애니메이션 동안에 ValueAnimator가 계산한 값을 이 callback에서 사용해야 함.
    • 값을 사용하기 위해서는 callback에 전달 된 ValueAnimator object에 getAnimatedValue()를 통해 값을 얻음.
    • 위에 설명한 것 처럼 object, property에 따라서 invalidation을 위한 method call이 필요할 수 있음

만약 Animator.AnimatorListener interface를 구현하여 모든 callback을 구현하길 원치 않는다면, AnimatorListenerAdapter 를 상속할 수 있다. (개발자가 override 해서 사용할 수 있도록 모든 callback에 empty method를 제공)

 

ex>

ValueAnimatorAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim
.setDuration(250);
fadeAnim
.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
balls
.remove(((ObjectAnimator)animation).getTarget());
}

 

Animating Layout Changes to ViewGroups

LayoutTransition class를 이용하여 ViewGroup 내의 layout changes 에 대해서 animation 할 수 있다.

ViewGroup 내의 View들에 대해서 VIew가 삽입, 삭제, 보이도록 혹은 보이지 않도록 설정함에 따라서 animation 할 수 있다.

LayoutTransition object에는 setAnimator()를 통해 Animator object와 아래 LayoutTransition 상수를 파라미터로 전달하여 애니메이션 할 수 있다.

  • APPEARING - Container에서 보이게 될 때
  • CHANGE_APPEARING - Container에 새 item이 추가 됨으로써 변경 될 때
  • DISAPPEARING - Container에서 안보이게 될 때
  • CHANGE_DISAPPEARING - Containenr에서 다른 item이 삭제 됨으로써 변결 될 때

 

Layout Animation을 위해 위 4가지 Animation을 만들어 설정하거나 기본 Animation을 사용할 수 있다.

default 적용 은 xml에서 단지 Layout에 android:animateLayoutChanges="true" 추가 만으로 가능하다.

 



TypeEvaluator 의 사용

Android system이 알지 못하는 타입을 animate하길 원한다면 TypeEvaluator interface를 구현함으로써 가능하다.

Android known type은 IntEvaluator, FloatEvaluator, ArgbEvaluator이다.

TypeEvaluator interface를 구현할 때는 한가지 메소드만 구현하면 됨.

이 때, Interpolator가 setting되면 계산 되어 fraction으로 들어오기 때문에 fraction만 이용하면 됨.

 

ex>

public class FloatEvaluator implements TypeEvaluator {

public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}

 

Interpolator의 사용

Interpolator는 시간을 계산하여 fraction이라는 현재까지 흐른 시간을 전달.

fraction에 제시간 되간에 맞추어 계산하면 됨.

만약 Android에서 제공하는 Interpolator가 구미에 맞지 않으면 TimeInterpolator interface를 구현하면 됨.

 

AccelerateDecelerateInterpolator

          publicfloat getInterpolation(float input){
return(float)(Math.cos((input +1)*Math.PI)/2.0f)+0.5f;
}
          

LinearInterpolator

          publicfloat getInterpolation(float input){
return input;
}
          

Specifying KeyFrames

KeyFrame object는 time / value 쌍으로 이루어졌다. (특정 시간에 특정 값)

각 KeyFrame은 각 각 자신의 Interpolator를 가질 수 있다.

KeyFrame object 생성을 위해서는 ofInt(), ofFloat(), ofObject() 메소드 사용.

 

ex>

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
rotationAnim
.setDuration(5000ms);

 

Animating Views

android 3.0에서 Properties Animation이 등장하면서 View는 아래 속성을 갖게 됨.

  • translationX and translationY: These properties control where the View is located as a delta from its left and top coordinates which are set by its layout container.
  • rotation, rotationX, and rotationY: These properties control the rotation in 2D (rotation property) and 3D around the pivot point.
  • scaleX and scaleY: These properties control the 2D scaling of a View around its pivot point.
  • pivotX and pivotY: These properties control the location of the pivot point, around which the rotation and scaling transforms occur. By default, the pivot point is located at the center of the object.
  • x and y: These are simple utility properties to describe the final location of the View in its container, as a sum of the left and top values and translationX and translationY values.
  • alpha: Represents the alpha transparency on the View. This value is 1 (opaque) by default, with a value of 0 representing full transparency (not visible).

 

 



Declaring Animations in XML

The property animation system lets you declare property animations with XML instead of doing it programmatically. The following Android classes have XML declaration support with the following XML tags:

Both <animator> (ValueAnimator) and <objectAnimator> (ObjectAnimator) have the following attributes:

android:duration
The number of milliseconds that the animation runs. The default is 300 ms.
android:valueFrom and android:valueTo
The values being animated between. These are restricted to numbers (float or int) and color values (such as #00ff00). They can be float, int, colors, or any kind of Object when creating animations programmatically.
android:valueType
Set to either "floatType" or "intType". The default is "floatType" unless you specify something else or if thevaluesFrom and valuesTo values are colors.
android:startDelay
The delay, in milliseconds, before the animation begins playing (after calling start()).
android:repeatCount
How many times to repeat an animation. Set to "-1" to infinitely repeat or to a positive integer. For example, a value of "1" means that the animation is repeated once after the initial run of the animation, so the animation plays a total of two times. The default value is "0", which means no repetition.
android:repeatMode
How an animation behaves when it reaches the end of the animation. android:repeatCount must be set to a positive integer or "-1" for this attribute to have an effect. Set to "reverse" to have the animation reverse direction with each iteration or "repeat" to have the animation loop from the beginning each time.

The objectAnimator (ObjectAnimator) element has the additional attribute propertyName, that lets you specify the name of the property being animated. The objectAnimator element does not expose a target attribute, however, so you cannot set the object to animate in the XML declaration. You have to inflate the XML resource by calling loadAnimator() and callsetTarget() to set the target object unlike the underlying ObjectAnimator, before calling start().

The set element (AnimatorSet) exposes a single attribute, ordering. Set this attribute to together (default) to play all the animations in this set at once. Set this attribute to sequentially to play the animations in the order they are declared.

You can specify nested set tags to further group animations together. The animations that you want to group together should be children of the set tag and can define their own ordering attribute.

As an example, this XML code creates an AnimatorSet object that animates x and y at the same time, then runs an animation that fades an object out:

          <setandroid:ordering="sequentially">
<set>
<objectAnimator
android:propertyName="x"
android:duration="500"
android:valueTo="400"
android:valueType="int"/>
<objectAnimator
android:propertyName="y"
android:duration="500"
android:valueTo="300"
android:valueType="int"/>
</set>
<objectAnimator
android:propertyName="alpha"
android:duration="500"
android:valueTo="0f"/>
</set>
          

In order to run this animation, you must inflate the XML resources in your code to an AnimatorSet object, and then set the target objects for all of the animations before starting the animation set. Calling setTarget() sets a single target object for all children of the AnimatorSet.

 


 

 

 

ViewPropertyAnimator

 

 

3.0에서 소개된 ObjectAnimator를 사용하면, 약간의 코드 수정으로 사용자 VIew의 property들을 animate 할 수 있다.

Animator를 생성하여, duration이나 repetition 속성과 같은 부가적인 속성을 설정하고 시작하면 된다.

예를 들어 myView라는 오브젝트를 fade out하고 싶다면 alpha property를 변화 시키면 된다.

 

ObjectAnimator.ofFloat(myView, "alpha", 0f).start();

 

이것은 복잡하지 않을 뿐더러 이해하기도 쉽다.

animate 할 오브젝트의 정보를 가지고 animator를 생성하고, 시작하면, 설정한 properry가 animate 될 것이다.

하지만 여기서 성능을 고려한 보다 향상된 방법을 택할 수 있다.

 

ViewProperties에 있어서 3.0 animation model의 성능 향상의 관건은 3가지로 볼 수 있다.

properties에 속하지 않는 언어의 속성을 animation하는 것, 다수의 properties를 animation하는 것 등이다.

 

Android runtime은 property라는 컨셉을 가지고 있지 않기 때문에, ObjectAnimator는 property의 문자형식의 이름을 이용한 target object의 setter 호출을 이용한다.

예를 들면 문자열 alpha라는 문자열은 View의 setAlpha 메소드를 referencing하게 된다.

이 함수는 신뢰성 있는 reflection 또는 JNI를 통해 호출 된다. 그러나 여기는 overhead가 존재한다.

하지만 우리가 알고있는 오브젝트나 프로퍼티에서 (View에 있는 propery등), 우리는 좀더 나은 방법을 사용할 수 있다.

animation 될 각 프로퍼티와 제공되는 API를 사용하여 JNI나 reflection에서 발생하는 overhead 없이 object에 직접 값을 설정할 수 있다.

 

또다른 overhead 중 하나는 Animator 자체이다.

모든 animation이 single timing mechanism을 공유해도, timing event Processing overhead를 추가해서는 안된다.

그것들은 각각의 프로퍼티를 위한 같은 태스크를 수행하는 분리된 오브젝트 들이다.

이 태스크들은 만약 우리가 여러 프로퍼티를 수행하는 하나의 animation을 수행하기 전 시간을 알 수 있다면 하나로 묶어낼 수 있다.

 

지금의 system에서 이렇게 하기 위한 유일한 방법은 PropertyValuesHolder를 사용하는 것이다.

이 class는 여러가지 property들은 동시에 animate 하는 것은 물론, Animator 당 발생하던 overhead 도 줄이는 단일 Animator를 만들 수 있게 해 준다.

하지만 이러한 접근은 코드량을 증가시키고 간단한 동작에 대해서도 복잡성을 증가시키는 원인이 된다.

아래에서 말할 새로운 접근은 우리에게 여러 하나의 animation 안에 있는 여러가지 propertiy들을 하나로 묶을 수 있는 더 간단한 방법을 제시한다.

 

마지막으로, View의 이러한 각 property들은 object 자체와 그 부모 View에서 적절한 갱신을 보장하기 위해서 여러가지 Operation을 수행한다.

예를 들면, View에서 x의 이동은 parent가 적절하게 redraws를 수행할 수 있게 View가 차지했던 위치와, 새로 차지한 위치를 갱신한다. 유사하게, y의 이동은, view의 전, 후 위치를 갱신한다. 만약 이 속성들이 병렬적으로 animation 된다면, 여러가지 속성이 animation 될 것이기 때문에 그만큼의 자원을 차지하며 수행 될 것이다.

ViewPropertyAnimator 는 이를 더 효과적으로 수행해 준다.

 

ViewPropertyAnimator의 소개.

ViewPropertyAnimator 는 내부적으로 단일 Animator를 사용하여 여러가지 속성의 애니메이션을 병렬적으로 수행하는 간단한 방법을 제시한다. ViewPropertyAnimator가 property들을 위한 애니메이션 값을 계산하고, target VIew에 곧바로 적용하고 object를 적절히 갱신하기 때문에 일반적인 ObjectAnimator를 사용하는 것 보다 효울 적이다.

 

그만 떠들고, 우리가 전에 보았던 fading-out view 예제를 보자. 아래와 같이 사용하면 된다.

myView.animate().alpha(0);

아주 간결하고, 읽기 편할 뿐 아니라 다른 property animation을 묶기 편하다.

예를 들면 우리는 view의 x와 y를 500, 500으로 아래와 같이 옮길 수 있다.

myView.animate().x(500).y(500)

 

system의 마술은 View object의 animate()를 호출함으로써 시작된다. 이것은 ViewPropertyAnimator를 반환한다. 이를 이용하여 각 property를 설정하는 다른 메소드를 호출한다.

여기서 우리가 임의로 animation을 start()하지 않았음을 기억해 두자. 이 새로운 API는 animation의 시작을 묵시적으로 수행한다.

 

 



 

Animation에 대해 명시하면 바로 수행되는 것이다. 여기서 우리가 잘 눈치채지 못하는 사항이 하나 있다. 그것은 이것이 시작하기 전에 UI toolkit event queue에서 다음 번 update를 기다린다는 것이다. 이것이 ViewPropertyAnimator가 모든 명시된 animation을 모아 수행할 수 있는 메커니즘이다.

계속적으로 animation을 명시 하는 한 다름 프레임에 시작할 animation.집합에 계속적으로 추가하고 있는 것이다.

animation을 명시하는 작업을 끝내고, UI thread의 control을 끝내면, event queue 메커니즘이 발동하고, animation이 시작된다.

ViewPropertyAnimatior는 연쇄적 호출을 통해 animation의 grouping을 쉽게 해 준디ㅏ.

 

Performance Anxiety

이런 새로운 접근 방식에서 성능상의 이점은 alpha 속성을 변경하는 간단한 예제에서도 볼 수 있다.

ViewPropertyAnimator는 reflection이나 JNI 기술를 사용하지 않는다.

예를 들면, 예제에서 alpha() 메소드는 animation frame 당 View의 alpha 필드를 한번씩 직접 조작한다.

또 다른 예로,

화면상에서 View를 디오할 때, 아마도 아래와 같이 Object의 x, y position을 움직일 것이다.

 

ObjectAnimator animX =ObjectAnimator.ofFloat(myView,"x",50f);
ObjectAnimator animY =ObjectAnimator.ofFloat(myView,"y",100f);
AnimatorSet animSetXY =newAnimatorSet();

              animSetXY.playTogether(animX, animY);

              animSetXY.start();

 

 

위 코드는 두개의 독립적인 animation을 만들고 AnimatorSet을 이용하여 함께 수행하도록 한다.

이것은 AnimatorSet을 설정하고, x, y 속성을 animate하는 두개의 병렬 animator를 수행하야 하는 오버헤드가 있다.

이것을 대체하는 방법으로 여러가지 속성을 조합을 단일 Animator에 적용 할 수 있는 PropertyValuesHolder를 사용할 수 있다.

 

PropertyValuesHolder pvhX =PropertyValuesHolder.ofFloat("x",50f);
PropertyValuesHolder pvhX =PropertyValuesHolder.ofFloat("y",50f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();

 

위와 같은 방식의 접근은 다중 Animator에 의한 오버헤드를 피할 수 있으며, ViewPropertyAnimator를 사용하기 것보다는 아니지만 올바른 방법이다.

ViewPropertyAnimator를 사용하면 더욱 간단해 진다.

 

myView.animate().x(50f).y(100f);

 

다시한번 말하지만, 코드가 더욱 간단해 지고 가독성이 좋아진다.

또한, PropertyValuesHolder의 접근법 처럼 같은 단일 Animator를 사용한다. (모든 명시된 properties를 animate하는데 내부적으로 단일 Animator 사용)

 

하지만 여기 ViewPropertyAnimator를 사용하므로써 얻을 수 있는 또 다른 장점이 존재한다.

코드상으로 위 예제에서 명시되지는 않았지만, 각 속성을 설정하는 수고를 덜 수 있다. 일반적으로, View에서 setX(), setY() 함수가 불려지만, 일정향의 계산과 갱신작업이 수반된다.(view hierachy에서 올바른 위치에 view의 움직임을 갱신하기 위해서)

ViewPropertyAnimator는 animation frame 당 이런 계산을 1번만 한다. property 당 한번 하는 것과는 대조적이다. x, y property 들에 대해서 직접 접근해서 설정하며, x, y에 대해서 1번만 invalidation한다.

 

Animator example


 

위 비디오에서 왼쪽 상단의 버튼들은 하나가 눌린 후 다른것이 눌린다.

버튼이 눌림에 따라 동작하는 모습을 볼 수 있다.

물론 ViewPropertyAnimator를 사용한 것이다.

 

activity가 처음 시작되고, 일반적인 duration 보다 더 길게 설정하기 위해서 아래의 코드를 삽입한다.

 

    animatingButton.animate().setDuration(2000);

 

video에서 animation을 충분히 보여주기 위함이다.

 

다음에 이어지는 code stub들은 단지 OnClickListener 시리즈 들이다.

처음에 FadeOut 버튼이 클릭되므로 button이 fade out 된다.

 

    fadeOut.setOnClickListener(newView.OnClickListener(){
@Override
publicvoid onClick(View v){

                      animatingButton.animate().alpha(0);
}
});

이 코드에서 단순히 오브젝트에 alpha 0으로 animate 하라고 하고 있음을 알 수 있다.

이렇게 하면 현재 alpha 값이 무엇이든 그 값을 기준으로 시작할 것이다.

다음 버튼은 Fade in action이다. alpha(1)을 줌으로 써 fully opaque(불투명)으로 만든다.

 

    animatingButton.animate().alpha(1);

MoveOver와 MoveBack button들은 x, y 두 property들은 병렬적으로 수행한다.

animator 에서 두 속성 메소드를 연결해서 부름으로써 수행된다.

 



MoveOver는

 

int xValue = container.getWidth()- animatingButton.getWidth();
int yValue = container.getHeight()- animatingButton.getHeight();

              animatingButton.animate().x(xValue).y(yValue);

와 같이 수행되며

MoveBack은

 

  animatingButton.animate().x(0).y(0);

와 같이 수행된다. (원위치로 돌림)

 

video로 부터 말하고자 하는 것 중 하나는, Move Over 수행 중 MoveBack을 중첩하여 수행 했다는 것이다. 같은 속성(x, y)에 두번째 지정 된 animation은 첫번째 animation을 cancel 하고, 두번째로 새로 지정 된 animation을 바로 첫번째 animation이 cancel된 지점에서 수행한다는 것이다.

이것은 ViewPropertyAnimator의 의도된 부분이다.

사용자의 명령대로 속성의 animate를 수행하고, 필요하다면, 현재 수행중이던 속성의 animation을 취소하고 나서 사용자 명령의 animation을 수행한다.

 

마지막으로, 버튼이 Y축을 기준으로 두번 회전하는 3D animation에 대해서 알아보자.

이것은 복잡하며 다른 animation에 비해서 더 많은 코드를 수반할 것이 명백하다.

 

 

animatingButton.animate().rotationYBy(720);

video의 rotation animation에서 밝히고자 하는 중요한 사항 하나는 Move Over 버튼 후 Rotation button을 누른것이다.

이것은 movement 상태를 변경 하면서 rotation 상태를 적용하게 된다. Move Over가 끝나고 나서도 Rotation animation이 남아 있다면 수행되며, 반대도 마찬가지이다.

이것은 완전히 독립된 ObjectAnimator를 내부적으로 생성하는 각각의 animation(동시에 같은 animator에 대해서 group화 되지 않은)이 어떻게 병렬적으로 독릭된 animation을 수행하는 가를 보여준다.

반응형

댓글