How to Create a Shimmer Loading Effect in Jetpack Compose (WITHOUT Library!)
Modifier 확장함수를 구현해 해당 확장함수만 Modifier 에 적용하면, 스켈레톤 이미지가 되는 로직이다.
Modifier.Extention
// 반짝임 효과 확장함수
fun Modifier.shimmerEffect(): Modifier = composed {
// 실제 컴포즈의 크기를 추적한다.
var size by remember {
mutableStateOf(IntSize.Zero)
}
// X축으로 이동하는 무한 애니메이션 그라데이션을 정의한다.
// 초기값은 크기의 -2배
// 목표값은 크기의 2배
val transition = rememberInfiniteTransition(label = "로딩중")
val startOffsetX by transition.animateFloat(
initialValue = -2 * size.width.toFloat(),
targetValue = 2 * size.width.toFloat(),
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 1000)
),
label = "로딩중"
)
// 그라데이션은 0, 0에서 시작하여 크기의 너비와 높이까지 이동한다.
background(
brush = Brush.linearGradient(
colors = listOf(
Color(0xFFB8B5B5),
Color(0xFF8F8B8B),
Color(0xFFB8B5B5)
),
start = Offset(startOffsetX, 0f),
end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat())
)
).onGloballyPositioned { size = it.size }
}
스켈레톤 화면 컴포즈 :
@Composable
fun SkeletonListItem(
modifier: Modifier = Modifier,
isLoading: Boolean,
contentAfterLoading: @Composable () -> Unit
) {
if(isLoading) {
Row(modifier = modifier) {
Box(
modifier = Modifier
.size((100.dp))
.shimmerEffect()
)
Spacer(modifier = Modifier.width(16.dp))
Column(
modifier = Modifier.weight(1f)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(20.dp)
.shimmerEffect()
)
Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier
.fillMaxWidth(0.7f)
.height(20.dp)
.shimmerEffect()
)
}
}
}
else {
contentAfterLoading()
}
}
실제 표시될 컨텐츠 :
@Composable
fun Test() {
var isLoading by remember { mutableStateOf(true) }
LaunchedEffect(Unit) {
delay(10000)
isLoading = false
}
LazyColumn(modifier = Modifier.fillMaxSize().background(Color.White)) {
items(20) {
SkeletonListItem(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
isLoading = isLoading
) {
Row(modifier = Modifier
.fillMaxWidth()
.padding(16.dp)) {
Icon(
modifier = Modifier.size(100.dp),
imageVector = Icons.Default.Home,
contentDescription = null
)
Spacer(modifier = Modifier.width(16.dp))
Text(
text = "This is a long text to show that out shimmer effect is working properly",
)
}
}
}
}
}