본문 바로가기
Android/애니메이션

[Android] 안드로이드 transition animation

by 붕어사랑 티스토리 2022. 1. 7.
반응형

https://developer.android.com/training/transitions

 

전환을 사용하여 레이아웃 변경 애니메이션 처리  |  Android 개발자  |  Android Developers

전환을 사용하여 레이아웃 변경 애니메이션 처리 Android의 전환 프레임워크를 사용하면 단순히 시작 레이아웃과 종료 레이아웃을 제공하여 UI에서 모든 종류의 모션을 애니메이션으로 보여줄 수

developer.android.com

 

 

1. Android Transition Framework

Transition Animation이란 하나의 프래그먼트에 두개의 레이아웃을 정의 한 후 두 레이아웃간에 애니메이션을 넣는 기법이다.

 

안드로이드 transition framework는 startting layout과 ending layout을 이용하여 레이아웃에 애니메이션 효과를 줄 수 있다.

 

 

고로 두가지만 기억하자

 

starting layout

 

ending layout

 

 

 

 

Transition Framework는 다음과 같은 기능을 가진다

 

  • Group level animation : 하나이상의 애니메이션을 View hierarchy에 있는 모든 View에 적용할 수 있다
  • Built-in animation : fade out이나 movement같은 미리 정의된 애니메이션을 쓸 수 있다
  • Resource file support : layout resouce 파일에서 View hierarchy와 built-in animation을 가지고 올 수 있다
  • Lifecycle callbacks : 애니메이션을 컨트롤하는 callback을 사용할 수 있다. 보통 hierarchy에 변화가 일어날 때 사용된다.

 

 

 

 

 

2. 애니메이션의 기본 프로세스

애니메이션은 기본적으로 다음과 같이 동작된다

 

1. starting layout과 ending layout에 대한 Scene Object를 각각 만든다. 그러나 보통 starting laytout의 Scene 오브젝트는 보통 현재 레이아웃 기반으로 자동으로 결정된다.

2. 원하는 애니메이션 타입의 Transition Object를 만든다.

3. TransitionManager.go() 메소드를 호출하면 두 레이아웃간 애니메이션이 진행된다.

 

안드로이드 애니메이션 기본적인 프로세스

 

 

 

3. Scene 생성하기

Scene은 view hierarchy의 state를 저장한다. 여기서 state란 hierarchy의 모든 뷰와 프로퍼티 값을 의미한다.

그리고 Transition framework는 starting scene과 ending scene을 이용하여 애니메이션을 동작시킨다.

 

Scene을 생성하는 방법은 두가지가 있다

 

  • layout resource 파일에서 scene을 생성한다
  • 코드상에서 scene을 생성한다

 

그러나 앞서 말했듯이 starting scene은 보통 현재 UI에 의해 자동적으로 결정된다.

 

 

Scene은 또한 Scene이 바뀔때 action이라는걸 정의할 수 있다. 예를들면 이 action이라는 기능은 scene의 transition이 끝나고 view settings를 clean up 할 때 유용하다.

 

 

 

4. 예제

 

다음 예제에서는 다음과 같은 layout이 구성되어 있다

 

  • 하나의 child layout이 있는 메인 레이아웃
  • 두개의 text field가 있는 relative layout, 그리고 이 레아이웃은 first scene이 된다
  • first scene처럼 두개의 text field가 있지만 순서가 바뀐 second scene 레이아웃
  • 텍스트 위젯을 클릭하면 두개의 위젯이 자동으로 바뀐다.

 

위 구성에서 추측 할 수 있듯이 앞으로의 예제에서는 메인 레이아웃의 child layout이 first scene과 second scene이 바뀌는 애니메이션을 보여 줄 것이다.

 

 

아래예제에서는 Transition이 제외되어있다.

 

 

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/master_layout">
    <FrameLayout
        android:id="@+id/scene_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include layout="@layout/first_scene"/>
    </FrameLayout>

</LinearLayout>

위 레이아웃은 메인 레이아웃 파일이다. 보시다시피 하나의 text field와 하나의 child 레이아웃을 가지고 있다.

그리고 initail로 지정된 레이아웃은 아래 예제에 나올 firsr scene의 레이아웃이다.

 

 

res/layout/first_scene.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent" >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/text_view1"
        android:textSize="32sp"
        android:text="Text Line 1" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/text_view2"
        android:textSize="32sp"
        android:layout_toRightOf="@+id/text_view1"
        android:text="Text Line 2" />
</RelativeLayout>

first scene의 레이아웃이다. 두개의 텍스트 필드가 있다.

 

 

res/layout/second_scene.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/text_view2"
        android:textSize="32sp"
        android:text="Text Line 2" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/text_view1"
        android:textSize="32sp"
        android:layout_toRightOf="@+id/text_view2"
        android:text="Text Line 1" />
</RelativeLayout>

second scene의 레이아웃이다. first scene과 비교해보면 텍스트 필드의 순서가 바뀌어 있다.

 

 

 

MainActivity.kt

class MainActivity : AppCompatActivity() {
    lateinit var textView1:TextView
    lateinit var textView2:TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val sceneRoot: ViewGroup = findViewById(R.id.scene_root)
        val firstScene: Scene? = Scene.getSceneForLayout(sceneRoot,R.layout.first_scene,this)
        val secondScene: Scene? = Scene.getSceneForLayout(sceneRoot,R.layout.second_scene,this)

        firstScene!!.setEnterAction(Runnable {
            textView1 = findViewById(R.id.text_view1)
            textView2 = findViewById(R.id.text_view2)

            textView1!!.setOnClickListener(View.OnClickListener {
                Log.i("boungUh","text1 clicked")
                TransitionManager.go(secondScene)
            })

            textView2!!.setOnClickListener(View.OnClickListener {
                Log.i("boungUh","text2 clicked")
                TransitionManager.go(firstScene)
            })
        })
        secondScene!!.setEnterAction(Runnable {
            textView1 = findViewById(R.id.text_view1)
            textView2 = findViewById(R.id.text_view2)

            textView1!!.setOnClickListener(View.OnClickListener {
                Log.i("boungUh","text1 clicked")
                TransitionManager.go(secondScene)
            })

            textView2!!.setOnClickListener(View.OnClickListener {
                Log.i("boungUh","text2 clicked")
                TransitionManager.go(firstScene)
            })
        })

        textView1 = findViewById(R.id.text_view1)
        textView2 = findViewById(R.id.text_view2)

        textView1!!.setOnClickListener(View.OnClickListener {
            Log.i("boungUh","text1 clicked")
            TransitionManager.go(secondScene)
        })
    }
}

 

 

 

 

 

5. 레이아웃 파일로부터 Scene 생성하기

다음 코드로 레이아웃 파일로부터 Scene을 생성 할 수 있다

 

 

코틀린

val sceneRoot: ViewGroup = findViewById(R.id.scene_root)
val firstScene: Scene? = Scene.getSceneForLayout(sceneRoot,R.layout.first_scene,this)
val secondScene: Scene? = Scene.getSceneForLayout(sceneRoot,R.layout.second_scene,this)

 

6. 코드에서 직접 Scene 생성하기

다른 방법으로는 코드에서 직접 레이아웃을 생성할 수 있다.

이 방법은 코드상에서 View hierarchy를 자주 바꿀 때 dynamic 하게 대응하는게 필요하면 사용하는것이 좋다.

 

방법은 다음과 같다

 

1. View hierarchy에서 메인 레이아웃과 애니메이션으로 사용될 레이아웃을 얻는다.

2. Scene 생성자를 이용하여 Scene을 생성한다

 

아래 코드는 실제로 돌아가는게 아니고 대략적인 플로우를 설명하는 예제이다.

 

코틀린

val sceneRoot = someLayoutElement as ViewGroup
val viewHierarchy = someOtherLayoutElement as ViewGroup
val scene: Scene = Scene(sceneRoot, viewHierarchy)

 

 

 

7. Scene Action 만들기

앞서 말한것 처럼 안드로이드 transition framework는 시스템이 scene에 들어갈때와 나올때 action이라는 것을 정의해 줄수 있다.

 

이러한 Scene Action은 다음과 같은 케이스에서 유용하다

 

  • 다른 View Hierarchy에 속한 View들간의 애니메이션을 다룰 때 유용하다. starting & ending scene에 각각 exit action & entry action 을 달아줄 수 있다
  • transition framework가 animation을 자동으로 줄 수 없는 view(ListView와 같은) 들에게 유용하다. 자세한 사항은 Limitation을 항목을 참고.(이 글 가장 아래에 적어둠)

 

 

custom scene action을 만드는 방법은 다음과 같다

 

  • Action으로 수행할 내용을 Runnable 오브젝트로 생성한다
  • 생성한 Runnable을 Scene.setExitAction() 또는 Scene.setEnterAction() 함수로 전달한다

이렇게 하면 프레임워크가 애니메이션 시작 전에 setExitAction()을 먼저 호출하고 애니메이션이 끝나면 setEnterAction() 함수를 호출한다.

 

 

 

순서에 주의하자! Exit가 먼저고 Entry가 나중이다.

 

first scene이 exit 한 뒤에 second scene이 enter 한다! 라고 외우면 편할 것이다.

 

 

8. Animation 이후 해결해야 할 문제

Scene A에서 Scene B로 넘어갔다고 하자. Scene A에서 설정한 세팅들은 Scene B에서 안먹힌다.

왜냐하면 Scene B로 화면이 넘어갔기에 더이상 Scene A에서 사용된 뷰 들이 보이지 않기 때문이다.

이때문에 우리는 앞서 배웠던 setEnterAction()을 활용하여 다시 전부다 세팅 해 주어야 한다

 

 

 

9. Transition 사용법

Transition 애니메이션을 실행하기 위해서는 Transition 오브젝트가 필요하다.

 

Transition 오브젝트는 subclass를 통하여 생성된다.

 

예를들면 AutoTransition or Fade 아니면 custom defined 된 transition을 통해 객체를 생성한다!

 

그리고 Scene과 생성한 Transition을 TransitionManager에게 넘겨주고 TransitionManager.go() 함수를 호출하면 애니메이션이 실행된다.

 

 

자 위에 전체 내용을 정리하면 다음과 같다

 

1. Start Scene과 Ending Scene을 준비한다

2. 원하는 애니메이션효과를 가진 Transition 객체를 생성한다.

3. Scene과 Transition 객체를 TransitionManager에게 넘기고 TransitionManager.go() 함수를 호출한다

 

 

 

10. Transition 생성하기

transition을 생성하는 방법은 두가지 방법이 있다.

 

1. XML에 정의하여 Transtion에 생성

2. 코드상에서 직접 Transition을 생성

 

 

 

1. XML에 Transition 정의하기

다음은 fade transition을 문자열을 xxml에 모아놓듯이 xml에 정의하는 예제이다.

 

res/transition/fade_transition.xml

<fade xmlns:android="http://schemas.android.com/apk/res/android" />

위와 같이 Transition을 정의하고 아래와 같이 코드상에서 인스턴스를 받을 수 있다.

var fadeTransition: Transition =
    TransitionInflater.from(this)
                      .inflateTransition(R.transition.fade_transition)

 

 

2. 코드상에서 직접 생성하기

아래는 코드상에서 직접 Transition을 구현하는 예제이다.

var fadeTransition: Transition = Fade()

 

Transition을 정의 하였다면 아래와 같이 TransitionManager의 go 메소드를 이용하면 애니메이션을 실행한다.

 

TransitionManager.go(endingScene, fadeTransition)

 

앞선 예제에서 보았듯이 go 함수를 이용하면 Scene이 바뀌게 된다. start Scene에서 세팅한 세팅들은 더이상 ending Scene에 적용되지 않는다. 고로 enterAction을 통해 다시 세팅을 해 주어야 한다.

 

 

11. Target 정하기

기본적으로 transition은 모든 view를 대상으로 애니메이션을 진행한다. 그리고 애니메이션의 대상이되는 view들을 traget이라고 한다.

 

transition은 기본적으로 target이 없이 시작하면 traget이 없다면 모든 view를 대상으로 애니메이션을 시작한다.

 

만약 일부 view들만 애니메이션의 대상이 되도록 하고 싶다면 removeTarget() 함수를 이용하여 필요없는 view들을 제거해주자.

 

반대로 특정 애니메이션만 target이 되고 싶도록 한다면 addTarget() 메소드를 이용하면 된다.

 

 

 

12. 여러개의 Transition 정의하기

아래 xml에서 처럼 여러개의 Transition을 한번에 정의할 수 있다.

 

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="sequential">
    <fade android:fadingMode="fade_out" />
    <changeBounds />
    <fade android:fadingMode="fade_in" />
</transitionSet>

 

이 xml은 TransitionSet 오브젝트로 받아 주어야 한다. 그리고 코드상에서 xml을 받으려면 TransitionInflater.from() 메소드를 이용하자.

 

TransitionSet은 Transition 클래스를 상속받기에 Transition 처럼 사용하면 된다.

 

 

 

 

 

 

반응형

댓글