- 위와 같은 형태의 동적 가이드를 Compose 기반으로 구현했다.
- 애니메이션을 제공하는 JSON 파일의 경우, 디자이너분으로부터 제공받은 점 참고.
- JSON 파일의 경우 res - raw 폴더를 생성하여 넣어주면 된다. (압축하지 않고, 변형 없이 앱에 들어가야 하는 파일들)
- 영상을 보면, JSON 애니메이션이 바로 나타나는게 아니라 서서히 페이드인 되면서 나타난다.
- 이를 위해 Compose의 AnimatedVisibility를 활용한다.
AnimatedVisibility(
visible = showGuide,
enter = fadeIn(animationSpec = tween(1500)),
exit = ExitTransition.None
) { ...
- 뷰의 가시성을 조정할 때 애니메이션 효과를 줄 수 있는 Compose API다.
- visible 필드에 remember 변수를 넣음으로서 보이기 / 안보이기의 갱신 처리가 가능하다.
- enter에는 진입 효과를 지정하고, exit에는 종료 효과를 지정할 수 있다.
- EnterTransition는 AnimatedVisibility에서 활용 가능한 각종 효과를 가진 클래스이다. 대충 아래 요소들을 담고 있다.
EnterTransition defines how an AnimatedVisibility
Composable appears on screen as it becomes visible.
The 4 categories of EnterTransitions available are:
fade: fadeIn
scale: scaleIn
slide: slideIn, slideInHorizontally, slideInVertically
expand: expandIn, expandHorizontally, expandVertically
EnterTransition. None can be used when no enter transition is desired.
Different EnterTransitions can be combined using plus operator, for example:
See Also:
fadeIn, scaleIn, slideIn,
slideInHorizontally, slideInVertically, expandIn, expandHorizontally,
expandVertically, AnimatedVisibility
- enter에만 페이드인 효과를 주고, exit 시에는 사용자 인터렉션으로 바로 사라지게 하기 위해 None을 지정했다.
- 가시성 상태를 정해줄 showGuide는 다음과 같이 선언되었다.
var showGuide by remember { mutableStateOf(false) }
LaunchedEffect() {
delay(400)
showGuide = true
}
BackHandler(enabled = showGuide) {
showGuide = false
}
- LaunchedEffect에서 초기 delay를 주고 가이드가 보일 수 있도록 구성했다.
- 또한 BackHandler를 통해 컴포저블 Context에서 뒤로 가기 제스처를 취하면 가이드가 사라지도록 했다.
- 이제 어두운 배경(Dim) 안에 JSON 애니메이션이 나타나도록 구성한다. (Dim 컴포넌트에 대해서는 별도 게시글로 다루겠습니다.)
AnimatedVisibility(
visible = showGuide,
enter = fadeIn(animationSpec = tween(400)),
exit = ExitTransition.None
) {
Dim(~~~~) {
var showLayout by remember { mutableStateOf(false) }
var showLottie by remember { mutableStateOf(false) }
LaunchedEffect(key1 = true) {
delay(400)
showLayout = true
delay(800)
showLottie = true
}
...
- 애니메이션을 잘 보면 Text와 FAB로 해당되는 일반 View가 먼저 보이고, 그다음에 터치 애니메이션이 보이게 된다.
- 이 또한 각 레벨로 AnimatedVisibility와 delay를 통해 순차적으로 보여줄 수 있도록 한다.
- 먼저 Text + FAB View
AnimatedVisibility(
visible = showLayout,
enter = fadeIn(animationSpec = tween(1500)),
exit = ExitTransition.None
) {
Column {
Text(
modifier = Modifier.padding(end = 15.dp),
text = title,
...
)
Spacer(modifier = Modifier.size(15.dp))
FAB(~~~)
}
}
- 다음으로 JSON 애니메이션이 동작할 Lottie Composable을 포함한 부분
AnimatedVisibility(
visible = showLottie,
enter = fadeIn(animationSpec = tween(1500)),
exit = ExitTransition.None
) {
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.addcoach))
val progress by animateLottieCompositionAsState(
composition,
true,
iterations = LottieConstants.IterateForever
)
LottieAnimation(
composition,
progress = { progress },
modifier = Modifier
.width(100.dp)
.height(100.dp)
.align(Alignment.BottomEnd)
.absoluteOffset(x = (5.5).dp, y = (-37).dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
onClick.invoke()
showGuide = false
showLayout = false
showLottie = false
}
)
}
- rememberLottieComposition을 통해 Lottie로 보여줄 Raw 리소스를 지정한다.
- animateLottieCompositionAsState로는 Lottie 애니메이션의 동작 상태를 지정한다. 바로 시작하고, 무한 재생으로 등록했다.
- 그렇게 구성한 composition과 progress를 LottieAnimation에 넣어주고, modifier를 통해 onClick 시, 가이드를 해제하도록 한다. 즉, 해당 애니메이션의 목적을 사용자가 직접 달성해야 핮만 가이드가 해제되는 것이다.
LottieAnimation(
composition,
progress = { progress },
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
onClick.invoke()
showGuide = false
showLayout = false
showLottie = false
}
)
- 만일 사용자가 가이드를 올바르게 진행하지 않고 외부 영역을 클릭했을 때에는 상위 전체를 덮고 있는 Dim 또는 해당 컴포저블의 modifier - onClickable에서 구성해주면 된다.
Dim(modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
//TODO : showToast("안내를 따라하면 없어집니다.")
}