문제점
기존 프로젝트에서 단계를 표시하는 상태바 Ui를 동적으로 바꾸고 싶었으나 적합한 라이브러리가 없어 직접 만들었다.
해결 방법
Animatable을 이용해 이전 Step과 이동하고자 하는 현재 Step Row안에 있는 Box width 값을 변경시켜 막대가 움직이는 것 같은 효과를 줬다.
prevStep 변수를 활용해
막대가 왼쪽에서 오른쪽으로 움직이는지,
오른쪽에서 왼쪽으로 움직이는지 구분한다.
Box가 오른쪽에서 왼쪽으로 차오르거나,
왼쪽에서 오른쪽으로 줄어드는 에니메이션을 위해서 상황에 맞는 Row horizontalArrangement를 변경했다.
Animatable animationSpec 파라미터
durationMillis로 막대가 움직이는 속도를 조절하고,
easing으로 가속도를 이용해 좀 더 역동적인 에니메이션을 구현할 수 있다.
코드
@Composable
fun LinearStepIndicator(
stepSize: Int,
currentStep: Int
) {
val percent = remember { Animatable(0f) }
var prevStep by remember { mutableStateOf(currentStep) }
LaunchedEffect(currentStep) {
if(currentStep == prevStep) return@LaunchedEffect
percent.animateTo(
targetValue = 1f,
animationSpec = tween(
durationMillis = 250,
easing = FastOutSlowInEasing
)
)
prevStep = currentStep
percent.snapTo(0f)
}
Row(
modifier = Modifier.height(4.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start),
) {
for (index in 0 until stepSize) {
Row(
modifier = Modifier
.height(4.dp)
.weight(1f)
.background(RemindMaterialTheme.colorScheme.bg_subtle),
horizontalArrangement = when {
prevStep < currentStep && index == prevStep -> Arrangement.End
prevStep > currentStep && index == currentStep -> Arrangement.End
else -> Arrangement.Start
}
) {
Box(
modifier = Modifier
.background(RemindMaterialTheme.colorScheme.accent_default)
.fillMaxHeight()
.let {
when {
prevStep < currentStep && index == currentStep -> it.fillMaxWidth(percent.value)
prevStep > currentStep && index == currentStep -> it.fillMaxWidth(percent.value)
prevStep < currentStep && index == prevStep -> it.fillMaxWidth(1f - percent.value)
prevStep > currentStep && index == prevStep -> it.fillMaxWidth(1f - percent.value)
prevStep == currentStep && currentStep == index -> it.fillMaxWidth(1f)
else -> it
}
}
) {}
}
}
}
}
사용 예시 코드
@Composable
@Preview(showBackground = true)
fun LinearStepIndicatorPreview() {
var currentStep by remember { mutableStateOf(0) }
val maxStep = 2
Column(
modifier = Modifier.padding(20.dp)
) {
LinearStepIndicator(stepSize = maxStep, currentStep = currentStep)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
IconButton(onClick = {
if(currentStep > 0) currentStep--
}) {
Icon(
painter = painterResource(R.drawable.ic_arrow_left),
contentDescription = "",
modifier = Modifier.size(48.dp)
)
}
IconButton(onClick = {
if(currentStep < maxStep - 1) currentStep++
}) {
Icon(
painter = painterResource(R.drawable.ic_arrow_light),
contentDescription = "",
modifier = Modifier.size(48.dp)
)
}
}
}
}
결과물
참조
https://www.bam.tech/article/create-an-animated-instagram-like-progress-bar-with-jetpack-compose
'Android' 카테고리의 다른 글
[Android] StateFlow emit 한번 만 하는 문제 (0) | 2024.01.05 |
---|---|
[Android] 오픈 라이선스 목록 고지 쉽게 적용하기 feat.Compose (2) | 2023.12.07 |