Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

스프링 물리학으로 움직임 애니메이션화

물리학 기반 모션은 강제로 구동됩니다. 스프링력은 상호작용과 모션을 안내하는 힘입니다. 스프링력에는 감쇠 및 강성과 같은 속성이 있습니다. 스프링 기반 애니메이션에서 값과 속도는 각 프레임에 적용되는 스프링력에 따라 계산됩니다.

앱의 애니메이션이 한 방향으로만 느려지게 하려면 마찰 기반 플링(fling) 애니메이션을 대신 사용해 보세요.

스프링 애니메이션의 수명주기

스프링 기반 애니메이션에서 SpringForce 클래스를 사용하면 스프링의 강성, 감쇠 비율 및 최종 위치를 맞춤설정할 수 있습니다. 애니메이션이 시작되면 바로 스프링에서 각 프레임의 애니메이션 값과 속도를 강제로 업데이트합니다. 애니메이션은 스프링력이 평형 상태가 될 때까지 계속됩니다.

예를 들어 화면에서 앱 아이콘을 드래그한 다음 나중에 아이콘에서 손가락을 떼면 보이지는 않지만 익숙한 힘에 끌려 아이콘이 원래 위치로 돌아갑니다.

그림 1에서는 유사한 스프링 효과를 보여줍니다. 원 가운데에 있는 더하기 기호(+)는 터치 동작을 통해 적용되는 힘을 나타냅니다.

스프링 해제
그림 1. 스프링 해제 효과

스프링 애니메이션 빌드

애플리케이션의 스프링 애니메이션을 빌드하는 일반적인 단계는 다음과 같습니다.

다음 섹션에서는 스프링 애니메이션의 일반적인 빌드 단계를 자세히 설명합니다.

지원 라이브러리 추가

물리학 기반 지원 라이브러리를 사용하려면 다음과 같이 프로젝트에 지원 라이브러리를 추가해야 합니다.

  1. 앱 모듈의 build.gradle 파일을 엽니다.
  2. 지원 라이브러리를 dependencies 섹션에 추가합니다.

          dependencies {
              def dynamicanimation_version = "1.0.0"
              implementation 'androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version'
          }
          

    이 라이브러리의 현재 버전을 보려면 버전 페이지의 Dynamicanimation에 관한 정보를 참조하세요.

스프링 애니메이션 만들기

SpringAnimation 클래스를 사용하면 객체의 스프링 애니메이션을 만들 수 있습니다. 스프링 애니메이션을 빌드하려면 SpringAnimation 클래스의 인스턴스를 만들고, 객체, 애니메이션화할 객체의 속성 및 애니메이션이 정지할 선택적 최종 스프링 위치를 제공해야 합니다.

참고: 스프링 애니메이션을 만들 때 스프링의 마지막 위치는 선택사항입니다. 그러나 애니메이션을 시작하기 전에 정의해야 합니다.

Kotlin

    val springAnim = findViewById<View>(R.id.imageView).let { img ->
        // Setting up a spring animation to animate the view’s translationY property with the final
        // spring position at 0.
        SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0f)
    }
    

Java

    final View img = findViewById(R.id.imageView);
    // Setting up a spring animation to animate the view’s translationY property with the final
    // spring position at 0.
    final SpringAnimation springAnim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0);
    

스프링 기반 애니메이션은 보기 객체의 실제 속성을 변경하여 화면에서 보기를 애니메이션화할 수 있습니다. 시스템에서 사용할 수 있는 보기는 다음과 같습니다.

  • ALPHA: 보기의 알파 투명도를 나타냅니다. 값은 기본적으로 1(불투명)이며 값 0은 완전 투명(표시되지 않음)을 나타냅니다.
  • TRANSLATION_X, TRANSLATION_YTRANSLATION_Z: 이 속성은 레이아웃 컨테이너에서 설정한 왼쪽 좌표, 위쪽 좌표 및 높이로부터의 델타 값으로 보기의 위치를 제어합니다.
    • TRANSLATION_X은(는) 왼쪽 좌표를 나타냅니다.
    • TRANSLATION_Y은(는) 위쪽 좌표를 나타냅니다.
    • TRANSLATION_Z은(는) 높이를 기준으로 보기의 깊이를 나타냅니다.
  • ROTATION, ROTATION_XROTATION_Y: 이 속성은 중심점을 기준으로 3D 및 2D(rotation 속성)에서 회전을 제어합니다.
  • SCROLL_XSCROLL_Y: 이 속성은 소스 왼쪽 및 위쪽 가장자리의 스크롤 오프셋(픽셀 단위)을 나타냅니다. 페이지가 얼마나 스크롤되는지의 측면에서 위치도 표시합니다.
  • SCALE_XSCALE_Y: 이 속성은 중심점을 기준으로 보기의 2D 크기 조정을 제어합니다.
  • X, YZ: 컨테이너에서 보기의 최종 위치를 나타내는 기본 유틸리티 속성입니다.

리스너 등록

DynamicAnimation 클래스에서는 두 개의 리스너, OnAnimationUpdateListenerOnAnimationEndListener을(를) 제공합니다. 이러한 리스너는 애니메이션 값이 변경되고 애니메이션이 끝날 때 애니메이션의 업데이트를 수신 대기합니다.

OnAnimationUpdateListener

여러 보기를 애니메이션화하여 체인된 애니메이션을 만들려면 현재 보기의 속성이 변경될 때마다 콜백을 받도록 OnAnimationUpdateListener을(를) 설정할 수 있습니다. 콜백은 현재 보기의 속성에서 발생하는 변경사항을 기반으로 스프링 위치를 업데이트하도록 다른 보기에 알립니다. 리스너를 등록하려면 다음 단계를 실행합니다.

  1. addUpdateListener() 메서드를 호출하고 리스너를 애니메이션에 연결합니다.

    참고: 애니메이션을 시작하기 전에 업데이트 리스너를 등록해야 합니다. 그러나 애니메이션 값이 변경될 때 프레임별 업데이트가 필요한 경우에만 업데이트 리스너를 등록해야 합니다. 업데이트 리스너에서는 애니메이션이 별도의 스레드에서 실행되지 않도록 합니다.

  2. 호출자에게 현재 객체의 변경에 관해 알리도록 onAnimationUpdate() 메서드를 재정의합니다. 다음 샘플 코드에서는 OnAnimationUpdateListener의 전반적인 사용을 보여줍니다.

Kotlin

    // Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
    val (anim1X, anim1Y) = findViewById<View>(R.id.view1).let { view1 ->
        SpringAnimation(view1, DynamicAnimation.TRANSLATION_X) to
                SpringAnimation(view1, DynamicAnimation.TRANSLATION_Y)
    }
    val (anim2X, anim2Y) = findViewById<View>(R.id.view2).let { view2 ->
        SpringAnimation(view2, DynamicAnimation.TRANSLATION_X) to
                SpringAnimation(view2, DynamicAnimation.TRANSLATION_Y)
    }

    // Registering the update listener
    anim1X.addUpdateListener { _, value, _ ->
        // Overriding the method to notify view2 about the change in the view1’s property.
        anim2X.animateToFinalPosition(value)
    }

    anim1Y.addUpdateListener { _, value, _ -> anim2Y.animateToFinalPosition(value) }
    

Java

    // Creating two views to demonstrate the registration of the update listener.
    final View view1 = findViewById(R.id.view1);
    final View view2 = findViewById(R.id.view2);

    // Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
    final SpringAnimation anim1X = new SpringAnimation(view1,
            DynamicAnimation.TRANSLATION_X);
    final SpringAnimation anim1Y = new SpringAnimation(view1,
        DynamicAnimation.TRANSLATION_Y);
    final SpringAnimation anim2X = new SpringAnimation(view2,
            DynamicAnimation.TRANSLATION_X);
    final SpringAnimation anim2Y = new SpringAnimation(view2,
            DynamicAnimation.TRANSLATION_Y);

    // Registering the update listener
    anim1X.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {

    // Overriding the method to notify view2 about the change in the view1’s property.
        @Override
        public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
                                      float velocity) {
            anim2X.animateToFinalPosition(value);
        }
    });

    anim1Y.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {

      @Override
        public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
                                      float velocity) {
            anim2Y.animateToFinalPosition(value);
        }
    });
    

OnAnimationEndListener

OnAnimationEndListener에서는 애니메이션의 끝을 알립니다. 애니메이션이 평형 상태가 되거나 취소될 때마다 콜백을 수신하도록 리스너를 설정할 수 있습니다. 리스너를 등록하려면 다음 단계를 수행합니다.

  1. addEndListener() 메서드를 호출하고 리스너를 애니메이션에 연결합니다.
  2. 애니메이션이 평형 상태가 되거나 취소될 때마다 알림을 받도록 onAnimationEnd() 메서드를 재정의합니다.

리스너 삭제

애니메이션 업데이트 콜백 및 애니메이션 종료 콜백 수신을 중지하려면 각각 removeUpdateListener()removeEndListener() 메서드를 호출합니다.

애니메이션 시작 값 설정

애니메이션의 시작 값을 설정하려면 setStartValue() 메서드를 호출하고 애니메이션의 시작 값을 전달합니다. 시작 값을 설정하지 않으면 애니메이션에서 객체의 현재 속성 값을 시작 값으로 사용합니다.

애니메이션 값 범위 설정

속성 값을 특정 범위로 제한하려면 최소 및 최대 애니메이션 값을 설정할 수 있습니다. 또한 알파(0 ~ 1)와 같은 고유 범위가 있는 속성을 애니메이션할 때도 범위를 제어하는 데 도움이 됩니다.

  • 최소값을 설정하려면 setMinValue() 메서드를 호출하고 속성의 최소값을 전달합니다.
  • 최대값을 설정하려면 setMaxValue() 메서드를 호출하고 속성의 최대값을 전달합니다.

두 메서드 모두 값이 설정된 애니메이션을 반환합니다.

참고: 시작 값을 설정하고 애니메이션 값 범위를 정의한 경우 시작 값이 최소값 및 최대값 범위에 있는지 확인합니다.

시작 속도 설정

시작 속도를 통해 애니메이션 시작 시 애니메이션 속성이 변경되는 속도를 정의합니다. 기본 시작 속도는 초당 픽셀로 설정됩니다. 터치 동작 속도를 사용하거나 고정 값을 시작 속도로 사용하여 속도를 설정할 수 있습니다. 고정 값을 제공하도록 선택하면 초당 dp 값을 정의한 다음 초당 픽셀로 변환하는 것이 좋습니다. 초당 dp로 값을 정의하면 속도가 밀도 및 폼 팩터에 독립적일 수 있습니다. 값을 초당 픽셀로 변환하는 방법에 관한 자세한 내용은 초당 dp를 초당 픽셀로 변환 섹션을 참조하세요.

속도를 설정하려면 setStartVelocity() 메서드를 호출하고 속도를 초당 픽셀 단위로 전달합니다. 이 메서드는 속도가 설정된 스프링력 객체를 반환합니다.

참고: GestureDetector.OnGestureListener 또는 VelocityTracker 클래스 메서드를 사용하여 터치 동작의 속도를 검색하고 계산합니다.

Kotlin

    findViewById<View>(R.id.imageView).also { img ->
        SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
            …
            // Compute velocity in the unit pixel/second
            vt.computeCurrentVelocity(1000)
            val velocity = vt.yVelocity
            setStartVelocity(velocity)
        }
    }
    

Java

    final View img = findViewById(R.id.imageView);
    final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
    …
    // Compute velocity in the unit pixel/second
    vt.computeCurrentVelocity(1000);
    float velocity = vt.getYVelocity();
    anim.setStartVelocity(velocity);
    

초당 dp를 초당 픽셀로 변환

스프링의 속도는 초당 픽셀 단위여야 합니다. 고정 값을 속도의 시작으로 제공하도록 선택하면 초당 dp 값을 제공한 다음 초당 픽셀로 변환합니다. 변환하려면 TypedValue 클래스의 applyDimension() 메서드를 사용합니다. 다음 샘플 코드를 참조하세요.

Kotlin

    val pixelPerSecond: Float =
        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, resources.displayMetrics)
    

Java

    float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, getResources().getDisplayMetrics());
    

스프링 속성 설정

SpringForce 클래스에서는 감쇠비 및 강성과 같은 각 스프링 속성의 getter와 setter 메서드를 정의합니다. 스프링 속성을 설정하려면 스프링력 객체를 검색하거나 속성을 설정할 수 있는 맞춤 스프링력을 만드는 것이 중요합니다. 맞춤 스프링력을 만드는 데 관한 자세한 내용은 맞춤 스프링력 만들기 섹션을 참조하세요.

팁: 모든 setter 메서드에서 스프링력 객체를 반환하므로, setter 메서드를 사용하는 동안 메서드 체인을 만들 수 있습니다.

감쇠비

감쇠비는 스프링 진동이 점진적으로 감소하는 것을 나타냅니다. 감쇠비를 사용하여 한 탄성에서 다음 탄성으로 신속하게 진동이 감소되는 방식을 정의할 수 있습니다. 스프링을 감쇠시키는 방법은 다음 4가지가 있습니다.

  • 감쇠비가 1보다 크면 과도 감쇠가 발생합니다. 그러면 객체가 신속하게 정지 위치로 돌아갑니다.
  • 임계 감쇠는 감쇠비가 1일 때 발생합니다. 그러면 최단 시간 내에 객체가 정지 위치로 돌아갑니다.
  • 부족 감쇠는 감쇠비가 1 미만일 때 발생합니다. 그러면 객체가 여러 번 정지 위치를 지나친 다음 점진적으로 정지 위치에 도달합니다.
  • 비감쇠는 감쇠비가 0일 때 발생합니다. 그러면 객체가 영구적으로 진동할 수 있습니다.

스프링에 감쇠비를 추가하려면 다음 단계를 완료합니다.

  1. getSpring() 메서드를 호출하여 감쇠비를 추가할 스프링을 검색합니다.
  2. setDampingRatio() 메서드를 호출하고 스프링에 추가할 감쇠비를 전달합니다. 메서드는 감쇠비가 설정된 스프링력 객체를 반환합니다.

    참고: 감쇠비는 음수가 아닌 숫자여야 합니다. 감쇠비를 0으로 설정하면 스프링이 정지 위치에 도달하지 않습니다. 즉, 영구적으로 진동합니다.

시스템에서 사용할 수 있는 감쇠비 상수는 다음과 같습니다.

그림 2: 높은 탄성

그림 3: 중간 탄성

그림 4: 낮은 탄성

그림 5: 탄성이 없음

기본 감쇠비는 DAMPING_RATIO_MEDIUM_BOUNCY(으)로 설정됩니다.

Kotlin

    findViewById<View>(R.id.imageView).also { img ->
        SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
            …
            //Setting the damping ratio to create a low bouncing effect.
            spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
            …
        }
    }
    

Java

    final View img = findViewById(R.id.imageView);
    final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
    …
    //Setting the damping ratio to create a low bouncing effect.
    anim.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);
    …
    

강성

강성을 통해 스프링의 강도를 측정하는 스프링 상수를 정의합니다. 강성 스프링은 스프링이 정지 위치에 있지 않을 때 연결된 객체에 더 큰 힘을 가합니다. 스프링에 강성을 추가하려면 다음 단계를 완료합니다.

  1. getSpring() 메서드를 호출하여 강성을 추가할 스프링을 검색합니다.
  2. setStiffness() 메서드를 호출하여 스프링에 추가할 강성 값을 전달합니다. 이 메소드는 강성이 설정된 스프링력 객체를 반환합니다.

    참고: 강성은 양수여야 합니다.

시스템에서 사용할 수 있는 강성 상수는 다음과 같습니다.

그림 6: 높은 강성

그림 7: 중간 강성

그림 8: 낮은 강성

그림 9: 매우 낮은 강성

기본 강성은 STIFFNESS_MEDIUM(으)로 설정됩니다.

Kotlin

    findViewById<View>(R.id.imageView).also { img ->
        SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
            …
            //Setting the spring with a low stiffness.
            spring.stiffness = SpringForce.STIFFNESS_LOW
            …
        }
    }
    

Java

    final View img = findViewById(R.id.imageView);
    final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
    …
    //Setting the spring with a low stiffness.
    anim.getSpring().setStiffness(SpringForce.STIFFNESS_LOW);
    …
    

맞춤 스프링력 만들기

기본 스프링력을 사용하는 대신 맞춤 스프링력을 만들 수 있습니다. 맞춤 스프링력을 사용하면 여러 스프링 애니메이션에 동일한 스프링력 인스턴스를 공유할 수 있습니다. 스프링력을 만든 후 감쇠비 및 강성과 같은 속성을 설정할 수 있습니다.

  1. SpringForce 객체를 만듭니다.

    SpringForce force = new SpringForce();

  2. 각 메서드를 호출하여 속성을 할당합니다. 메서드 체인도 만들 수 있습니다.

    force.setDampingRatio(DAMPING_RATIO_LOW_BOUNCY).setStiffness(STIFFNESS_LOW);

  3. setSpring() 메서드를 호출하여 스프링을 애니메이션으로 설정합니다.

    setSpring(force);

애니메이션 시작

스프링 애니메이션을 시작할 수 있는 방법은 start()을(를) 호출하거나 animateToFinalPosition() 메서드를 호출하는 식의 두 가지 방법이 있습니다. 두 메서드 모두 기본 스레드에서 호출해야 합니다.

animateToFinalPosition() 메소드를 통해 다음 두 가지 작업을 완료합니다.

  • 스프링의 최종 위치를 설정합니다.
  • 애니메이션이 시작되지 않았으면 애니메이션을 시작합니다.

이 메서드에서 스프링의 마지막 위치를 업데이트하고 필요한 경우 애니메이션을 시작하므로 언제든지 이 메서드를 호출하여 애니메이션 과정을 변경할 수 있습니다. 예를 들어 연쇄 스프링 애니메이션에서 한 보기의 애니메이션은 다른 보기에 따라 달라집니다. 이러한 애니메이션에서는 animateToFinalPosition() 메서드를 사용하는 것이 더 편리합니다. 연쇄 스프링 애니메이션에서 이 메서드를 사용하면 다음에 업데이트하려는 애니메이션이 현재 실행 중인지 신경 쓰지 않아도 됩니다.

그림 10에서는 한 보기의 애니메이션이 다른 보기에 따라 달라지는 연쇄 스프링 애니메이션을 보여줍니다.

연쇄 스프링 데모
그림 10. 연쇄 스프링 데모

animateToFinalPosition() 메서드를 사용하려면 animateToFinalPosition() 메서드를 호출하고 스프링의 정지 위치를 전달합니다. setFinalPosition() 메서드를 호출해서도 스프링의 정지 위치를 설정할 수 있습니다.

start() 메서드에서는 속성 값을 즉시 시작 값으로 설정하지 않습니다. 속성 값은 각 애니메이션이 깜박일 때 변경되며 그리기 단계 이전에 발생합니다. 따라서 값이 즉시 설정되는 것처럼 변경사항이 다음 프레임에 반영됩니다.

Kotlin

    findViewById<View>(R.id.imageView).also { img ->
        SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
            …
            //Starting the animation
            start()
            …
        }
    }

    

Java

    final View img = findViewById(R.id.imageView);
    final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
    …
    //Starting the animation
    anim.start();
    …
    

애니메이션 취소

애니메이션을 취소하거나 애니메이션의 끝으로 건너뛸 수 있습니다. 애니메이션을 취소하거나 애니메이션의 끝으로 건너뛰어야 하는 경우는 사용자 상호작용에 따라 애니메이션을 즉시 종료해야 하는 경우입니다. 주로 사용자가 앱을 갑자기 종료하거나 보기가 표시되지 않는 경우입니다.

애니메이션을 종료하는 데 사용할 수 있는 메서드는 두 가지가 있습니다. cancel() 메서드에서는 값이 있는 위치에서 애니메이션을 종료합니다. skipToEnd() 메서드에서는 애니메이션을 최종 값까지 건너뛴 다음 종료합니다.

애니메이션을 종료하려면 먼저 스프링의 상태를 확인해야 합니다. 상태가 감쇠되지 않으면 애니메이션은 정지 위치에 도달할 수 없습니다. 스프링의 상태를 확인하려면 canSkipToEnd() 메서드를 호출합니다. 스프링이 감쇠되면 메서드에서 true를 반환하고, 감쇠되지 않으면 false를 반환합니다.

스프링의 상태를 알면 skipToEnd() 메서드 또는 cancel() 메서드를 사용하여 애니메이션을 종료할 수 있습니다. cancel() 메서드는 기본 스레드에서만 호출해야 합니다.

참고: 일반적으로 skipToEnd() 메서드를 사용하면 시각적으로 점프합니다.