Posts [Compose] Animation 사용해보기
Post
Cancel

[Compose] Animation 사용해보기

최근 GDG dev 2021을 통해서 Compose Codelab 공부를 하고 있는데, Animation을 사용하는 예제들을 따라해보고자 한다.

Jetpack Compose Animation

작성된 전체 코드

Animation

Animating a simple value change

animate*AsState 라는 API를 이용하면 값이 변경될 때 값이 변경된 값을 애니메이션화 해주는 것이라고 생각하면 된다.

1
2
3
4
5
6
@Composable
fun Home() {
    ...
    val backgroundColor by animateColorAsState(targetValue = if (tabPage == TabPage.Home) Purple100 else Green300)
    ...
}

위와 같은 코드를 통해 targetValue가 변화하는 것에 따라 background의 color가 변화하는 것을 볼 수 있다.




Animating visibility

boolean 값을 통해서 UI를 노출할지/말지에 대해 정하게 되고 이 상태가 변할 때 애니메이션을 보여주고자 할 때에 사용하게 된다.

또한, 머테리얼 디자인의 권장 사항에 맞게 상위 수준의 애니메이션의 api를 제공해주고 있다고 한다.

다만, 이 부분은 실험용 기능으로 사용되고 있는 api들이기 때문에 나중에 수정되거나 삭제될 수 있음을 유의하여야 한다.

코드랩에서는 Transition을 다루고 있는데 그 외에도 FadeIn, Sliding, Scaling 등의 애니메이션을 제공하고 있다고 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun HomeFloatingActionButton(
        ...
) {
  ...
  AnimatedVisibility(extended) {
    Text(
            text = stringResource(R.string.edit),
            modifier = Modifier
                    .padding(start = 8.dp, top = 3.dp)
    )
  }
}

AnimatedVisibility api를 통해서 extended라는 boolean 값의 변화에 따라서 해당 Text가 스르륵 등장했다가 스르륵 사라지는 애니메이션 효과가 생기는 것을 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun EditMessage(shown: Boolean) {
    AnimatedVisibility(
        visible = shown,
        enter = slideInVertically(
            initialOffsetY = { fullHeight -> -fullHeight },
            animationSpec = tween(durationMillis = 150, easing = LinearOutSlowInEasing)
        ),
        exit = slideOutVertically(
            targetOffsetY = { fullHeight -> -fullHeight },
            animationSpec = tween(durationMillis = 250, easing = FastOutLinearInEasing)
        )
    ) {
        ...
    }
}

위와 같이 AnimatedVisibility api에 enter, exit 파라미터를 통해서 커스텀도 가능하다.

위 예시에서는 edit 버튼을 누르면 상단에서 스르륵 글자가 나왔다가 위로 스르륵 사라지는 애니메이션을 구현한 모습을 볼 수 있다.




Animating content size Change

content의 사이즈가 변경될 때에 애니메이션 효과를 줄 수 있는 animateContentSize 함수에 대해 학습하고자 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun TopicRow(topic: String, expanded: Boolean, onClick: () -> Unit) {
  ...
  Surface(
          ...
  ) {
    Column(
            modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp)
                    .animateContentSize()
    ) {
        ...
    }
  }
  ...
}

animateContentSize api는 위와 같이 Modifier에 체인을 통해 사용할 수 있다.

api 이름처럼 content의 size가 변화할 때에 애니메이션이 동작하며 위 예시처럼 파라미터로 아무것도 넘겨주지 않으면 기본적으로 spring 애니메이션을 보여주게 된다.

이렇게 플랫폼에서 제공해주는 api들을 사용할 때에는 내부 구현을 한번쯤은 보는 것을 추천한다.
기본적으로 파라미터를 넘기지 않아도 spring 애니메이션이 나오는 것을 알 수 있는 이유도 내부 구현을 보았기 때문이다..!




Multiple value animation

updateTransition 함수를 통해서 Transition 인스턴스를 만들고, 상태를 저장하고, 상태를 업데이트 하는 과정을 거치게 된다.

이 때, transitionSpec 이라는 매개변수를 통해서 애니메이션 동작을 커스텀할 수 있다.

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
32
33
34
35
36
37
38
39
40
41
42
43
@Composable
private fun HomeTabIndicator(
    tabPositions: List<TabPosition>,
    tabPage: TabPage
) {
  val transition = updateTransition(targetState = tabPage, label = "Tab indicator")
  val indicatorLeft by transition.animateDp(
          transitionSpec = {
            if (TabPage.Home isTransitioningTo TabPage.Work) {
              // Indicator moves to the right.
              // The left edge moves slower than the right edge.
              spring(stiffness = Spring.StiffnessVeryLow)
            } else {
              // Indicator moves to the left.
              // The left edge moves faster than the right edge.
              spring(stiffness = Spring.StiffnessMedium)
            }
          },
          label = "Indicator left"
  ) { page ->
    tabPositions[page.ordinal].left
  }
  val indicatorRight by transition.animateDp(
          transitionSpec = {
            if (TabPage.Home isTransitioningTo TabPage.Work) {
              // Indicator moves to the right
              // The right edge moves faster than the left edge.
              spring(stiffness = Spring.StiffnessMedium)
            } else {
              // Indicator moves to the left.
              // The right edge moves slower than the left edge.
              spring(stiffness = Spring.StiffnessVeryLow)
            }
          },
          label = "Indicator right"
  ) { page ->
    tabPositions[page.ordinal].right
  }
  val color by transition.animateColor(label = "Border color") { page ->
    if (page == TabPage.Home) Purple700 else Green800
  }
  ...
}

위와 같은 코드를 사용해서 빌드를 해보면 탭 영역의 네모 박스가 더 역동적(?)으로 움직이는 것을 볼수 있다.

아직 위의 코드를 완벽히 이해하지 못했다… 😰




Repeated animation

InfiniteTransition 을 사용하면 MultiPle value animation 에서 학습한 것 처럼 여러 애니메이션을 묶어서 사용하는 것 처럼 비슷한 형태로 사용이 가능하다.

다만, 다른점은 애니메이션이 즉시 시작하며 삭제되지 않는한 중단되지 않는 애니메이션을 만들 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Composable
private fun LoadingRow() {
  val infiniteTransition = rememberInfiniteTransition()
  val alpha by infiniteTransition.animateFloat(
          initialValue = 0f,
          targetValue = 1f,
          animationSpec = infiniteRepeatable(
                  animation = keyframes {
                    durationMillis = 1000
                    0.7f at 500
                  },
                  repeatMode = RepeatMode.Reverse
          )
  )
  ...
}




Gesture animation

가장 하위 수준인 Animatable 은 터치, 드래그 등의 이벤트가 발생했을 때 애니메이션을 스탑하거나 하는 등의 제어를 할 수 있게 한다.

해당 코드는 길고 주석이 중요해서 링크를 통해 보는 것이 편할 것 같아 따로 코드를 적지 않았다.
코드 링크 를 참고하길.. 🙏




글을 마치며

지난번 커스텀 Layout에 이어서 이번엔 애니메이션이다. 안드로이드 개발을 하면서 어렵다고 생각한 부분 중 하나가 애니메이션인데 너무 간단하게(?) 코드 랩이 종료되는 것 같아 아쉽다.
그리고 실제 개발에서도 TouchEvent 같은 것들을 다룰 때에는 굉장히 조심스럽고 쉽지 않은데 코드랩에서는 생각보다 간단하게 넘어가서 이해도 쉽게 되지 않는다.
이 부분은 직접 사용해보고 깨우치는 과정이 추가적으로 필요할 것 같다. 역시나 어렵다 애니메이션.. 😰

참고 사이트
This post is licensed under CC BY 4.0 by the author.

[Compose] State 란?

-

Comments powered by Disqus.