안녕하세요.
오늘은 Jetpack Compose를 사용해서 진행중임을 표시할 수 있는 애니메이션 UI를 만들어보겠습니다.
Jetpack Compose란?
Jetpack Compose는 네이티브 UI를 빌드하기 위한 Android의 최신 툴킷입니다. 더 적은 수의 코드, 강력한 도구, 직관적인 Kotlin API로 Android에서의 UI 개발을 간소화하고 가속화하여 앱에 생동감을 더해줍니다. Android UI를 더 빠르고 쉽게 빌드할 수 있습니다.
1.Twin circle animation
1) UI 구성하기
가장 바깥에 위치한 원에 대한 Row를 만들고
내부에 쌍둥이 원에 대해 두개의 Box를 사용했으며
두 개의 쌍둥이 원 사이에 공간을 제공하는 Spacer를 사용했습니다.
Row( //제일 바깥 원
modifier = Modifier
.size(120.dp)
.padding(12.dp)
.clip(CircleShape)
.background(Red),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Box( //쌍둥이 원 1
modifier = Modifier
.size(15.dp)
.scale(twinCircleAnimation)
.clip(CircleShape)
.background(White)
)
Spacer(modifier = Modifier.width(6.dp)) //쌍둥이 원 사이의 공간
Box( //쌍둥이 원 2
modifier = Modifier
.size(15.dp)
.scale(twinCircleAnimation)
.clip(CircleShape)
.background(White)
)
}
2) 애니메이션
두 개의 작은 원에 애니메이션을 제공하는 twinCircleAnimation 입니다.
- infiniteTransition : 무한한 시간동안 계속 반복
- animateFloat : infiniteTransition의 일부로서 무한히 실행되는 float 타입의 애니메이션을 생성 (애니메이션이 생성되면 initial value (1f) 부터 target value (7f) 까지 생성되고 반복됩니다. )
- AnimationSpec : 일정한 속도로 움직이지 않게 속도를 높이거나 낮춰줍니다.
val twinCircleAnimation by infiniteTransition.animateFloat(
initialValue = 1f,
targetValue = 7f,
animationSpec = infiniteRepeatable(
animation = tween(1500, easing = FastOutSlowInEasing),
repeatMode = RepeatMode.Reverse
)
)
3) 사용해보기
전체 코드입니다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PreviewTwinCircleAnimation()
}
}
}
@Preview
@Composable
fun PreviewTwinCircleAnimation() {
Surface(
modifier = Modifier
.fillMaxSize(),
color = MaterialTheme.colors.background
) {
TwinCircleAnimation()
}
}
@Composable
fun TwinCircleAnimation() {
val infiniteTransition = rememberInfiniteTransition()
val twinCircleAnimation by infiniteTransition.animateFloat(
initialValue = 1f,
targetValue = 7f,
animationSpec = infiniteRepeatable(
animation = tween(1500, easing = FastOutSlowInEasing),
repeatMode = RepeatMode.Reverse
)
)
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Row(
modifier = Modifier
.size(120.dp)
.padding(12.dp)
.clip(CircleShape)
.background(Color.Red),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(15.dp)
.scale(twinCircleAnimation)
.clip(CircleShape)
.background(Color.White)
)
Spacer(modifier = Modifier.width(6.dp))
Box(
modifier = Modifier
.size(15.dp)
.scale(twinCircleAnimation)
.clip(CircleShape)
.background(Color.White)
)
}
}
}
2. Pacman animation
canvas로 팩맨 애니매이션을 만들어보겠습니다.
1) UI 만들기
canvas 내부에 팩맨 머리를 위한 하나의 arc(호)가 있고
팩맨 눈을 위한 하나의 원이 있습니다.
drawArc 내부에선 두 개의 애니메이션을 사용했는데 이것이 팩맨이 입을 열고 닫는 동작을 하도록 합니다.
Canvas(
modifier = Modifier
.padding(top = 16.dp)
.size(100.dp)
) {
drawArc( //팩맨 머리
color = Color(0xFFFFd301),
startAngle = antiMouthAnimation,
sweepAngle = mouthAnimation,
useCenter = true,
)
drawCircle( //팩맨 눈
color = Black,
radius = 15f,
center = Offset(x = this.center.x + 15f, y = this.center.y - 85f)
)
}
2) 애니메이션
mouth 애니메이션은 팩맨의 머리(호)를 360f에서 280f(입 위쪽)으로 회전하고
antiMouth 애니메이션은 Pacman 호를 0f에서 40f(입 아래쪽)으로 회전합니다.
val mouthAnimation by infiniteTransition.animateFloat(
initialValue = 360F,
targetValue = 280F,
animationSpec = infiniteRepeatable(
animation = tween(800, easing = FastOutSlowInEasing),
repeatMode = RepeatMode.Restart
)
)
val antiMouthAnimation by infiniteTransition.animateFloat(
initialValue = 0F,
targetValue = 40F,
animationSpec = infiniteRepeatable(
animation = tween(800, easing = FastOutSlowInEasing),
repeatMode = RepeatMode.Restart
)
)
3) 사용해보기
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PreviewPacmanAnimation()
}
}
}
@Preview
@Composable
fun PreviewPacmanAnimation() {
Surface(
modifier = Modifier
.fillMaxSize(),
color = MaterialTheme.colors.primary
) {
PacmanAnimation()
}
}
@Composable
fun PacmanAnimation() {
val infiniteTransition = rememberInfiniteTransition()
val mouthAnimation by infiniteTransition.animateFloat(
initialValue = 360F,
targetValue = 280F,
animationSpec = infiniteRepeatable(
animation = tween(800, easing = FastOutSlowInEasing),
repeatMode = RepeatMode.Restart
)
)
val antiMouthAnimation by infiniteTransition.animateFloat(
initialValue = 0F,
targetValue = 40F,
animationSpec = infiniteRepeatable(
animation = tween(800, easing = FastOutSlowInEasing),
repeatMode = RepeatMode.Restart
)
)
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Canvas(
modifier = Modifier
.padding(top = 16.dp)
.size(100.dp)
) {
drawArc(
color = Color.White,
startAngle = antiMouthAnimation,
sweepAngle = mouthAnimation,
useCenter = true,
)
drawCircle(
color = Color.Black,
radius = 15f,
center = Offset(x = this.center.x + 15f, y = this.center.y - 85f)
)
}
}
}
더 다양한 애니메이션이 궁금하다면 아래 깃헙을 참고해주세요
https://github.com/canopas/Jetpack-compose-animations-examples
'안드로이드 코틀린' 카테고리의 다른 글
[Kotlin][Android] Camposer: Jetpack Compose 카메라 라이브러리 사용하기 (0) | 2023.01.05 |
---|---|
[Kotlin][Android] Splash Screen(로딩 화면) 구현하기 (2) | 2022.12.23 |
[Kotlin] onBackPressed() deprecated , 대체할 메서드는? (0) | 2022.12.12 |
[Kotlin][Android] 키보드 열고 닫을 때 view를 부드럽게 이동시키기 (0) | 2022.12.03 |
[Kotlin][Android] DataBinding + RecyclerView 함께 사용해보기 (0) | 2022.11.18 |