2024.09.29 - [Android/Jetpack Compose] - [Android] Shimmer UI 구현하기
지난번 Shimmer UI를 구현한 이후, 이번에는 Text 자체의 Shimmer를 적용해야 하는 경우가 생겼다.
의외로 간단하게 구현이 가능하지만, 필자가 적용하기 위해 적용했던 단계를 작성해보고, Text에 shimmer를 적용하는 방법에 대해 알아보도록 하겠다.
기존에 사용했던 shimmer util은 위에 언급한 글을 보면 알 수 있겠지만, 다음과 같다.
fun Modifier.shimmer(
colorList: List<Color> = listOf(
Color.LightGray.copy(alpha = 0.2f),
Color.LightGray.copy(alpha = 0.9f),
Color.LightGray.copy(alpha = 0.2f)
),
shimmerAnimation: Float,
ratio: Float = 0.6f
): Modifier = composed {
val configuration = LocalConfiguration.current
val screenWidthDp = configuration.screenWidthDp.dp
val shimmerWidth = screenWidthDp * ratio
background(
brush = Brush.linearGradient(
colors = colorList,
start = Offset(
x = shimmerAnimation - shimmerWidth.value,
y = shimmerAnimation - shimmerWidth.value
),
end = Offset(x = shimmerAnimation, y = shimmerAnimation)
)
)
}
해당 modifier를 사용하여 background의 Brush 자체에 색상과 애니메이션을 주어 shimmer 기능을 구현하였다.
이를 text에도 동일하게 적용할 수 있는데 이를 사용하면 다음과 같다.
Box(
modifier = Modifier
.shimmer(
shimmerAnimation = translateAnim
)
) {
Text(
text = "Basic Shimmer Effect"
)
}
물론 이처럼 사용하면, 기존 UI에 shimmer를 넣어두고 그 위에 TextView를 올려서 보여주는 것과 동일하다.
하지만 필자는 Text를 제외한 Compose함수에 Background에 shimmer를 넣어서 사용하는 방식은 사용하고 싶지 않았다.
여기서 필자가 생각한 것은 다음과 같다.
Modifier.shimmer()를 확인해보면 brush와 애니메이션을 조합한 값을 사용하여 위와 같은 shimmer를 구현하고 있는데, text에 넣으면 되지 않을까?
우선 shimmer 자체를 Text의 Modifier에 넣어보았다.
Text(
text = "Shimmer Effect Using Modifier Only",
modifier = Modifier.shimmer(
shimmerAnimation = translateAnim
)
)
이와 같이 사용해도 위에 올려둔 동영상과 완전히 동일한 형태로 보이게 된다.
text의 Modifier는 text 자체의 속성이 아닌 Text View 자체의 속성을 변경해 주는 것이다.
그렇기 때문에 shimmer를 Text에 넣는다고 해서 원하는 동작을 하는 것이 아니라 Text View layer 부분의 속성이 변경된다.
필자가 원하는 동작을 하기 위해서는 Text 자체의 속성에 brush 값을 설정해줘야 하며,
Text 자체의 brush는 style 속성에 적용할 수 있으므로 이를 사용해보도록 하자.
Text(
text = "Dynamic Linear Gradient",
style = TextStyle(
brush = Brush.linearGradient(
colors = listOf(
Color.Gray,
Color.White,
Color.Gray,
),
start = Offset(translateAnim, 0f),
end = Offset(translateAnim + 100f, 100f),
)
),
)
shimmer와 동일하게 linearGradient를 사용하여 색상을 넣고, 기존에 shimmer에 사용하던 translateAnim을 사용하여 애니메이션을 설정해 주었다.
이렇게 설정을 해주면 다음과 같이 나오게 된다.
이렇게 필자가 원하는 대로 Text의 백그라운드에 shimmer가 들어가는 것이 아니라 Text 자체의 색상에 shimmer가 들어가게 된다.
원하는 동작을 완성하였으니, 해당 Style을 쉽게 사용할 수 있도록 함수로 만들어서 빼내도록 하자.
@Composable
fun textShimmer(
colors: List<Color> = listOf(
Color.LightGray,
Color.DarkGray,
Color.LightGray,
),
durationMillis: Int = 1000,
gradientWidth: Float = 4000f,
fontWeight: FontWeight = FontWeight.W400,
fontSize: TextUnit = 13.sp,
letterSpacing: TextUnit = 0.01.em,
lineHeight: TextUnit = 1.40.em
): TextStyle {
val transition = rememberInfiniteTransition(label = "shimmer")
val translateAnimation = transition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = durationMillis,
easing = LinearEasing
),
repeatMode = RepeatMode.Reverse
),
label = "textShimmer"
)
return TextStyle(
brush = Brush.linearGradient(
colors = colors,
start = Offset(0f, 0f),
end = Offset(translateAnimation.value * gradientWidth, 0f),
),
fontWeight = fontWeight,
fontSize = fontSize,
letterSpacing = letterSpacing,
lineHeight = lineHeight
)
}
여기서 사용한 TextStyle의 font 관련된 값들은 임의로 넣어준 것이므로 무시해도 상관없다.
여기서 중요한 부분은 translateAnimation 부분과 TextStyle의 Brush 부분이다.
위의 테스트를 하면서 사용했던 translateAnim 값을 함수 내부로 넣어서 사용하도록 변경한 부분을 제외하고는 사용하는 TextStyle을 그대로 함수로 만들어서 구현했을 뿐이다.
이렇게 함수로 구현한 다음에는
Text(
text = "Custom Shimmer with Style",
style = textShimmer()
)
이와 같이 간단하게 선언만 하면 원하는 textShimmer를 구현할 수 있게 된다.
이것으로 간단하게 이전에 만들어둔 shimmer 함수를 사용하여 textShimmer도 구현해 보았다.
단순하게 1차원적으로만 생각했을 때,
backgorund 색상이 바뀌는 것과 내부 content의 색상이 바뀌는 것은 다른 기능이 아닌가?라고 생각하였고 그냥 간단하게 shimmer만 modifier에 넣어서 어떻게 나오나 한번 확인해 보자.라고 적용을 해봤었다.
shimmer를 적용해 보고 나오는 UI를 보고, 실제 필자가 사용했던 Text Compose 함수를 생각해 보니
실제 Modifier가 적용되는 범위, 기능을 구현할 때 기능이 적용되어야 하는 범위가 다르고, TextStyle에 동일하게 넣으면 될 것 같아 그냥 한번 해보니 간단하게 되어서 쉽게 기능을 구현할 수 있었다.
최근에 여러 경험과, 이번 기능을 구현하면서 다시 한번 느낀 것이,
전반적으로 우리가 원하는 기능을 구현하는 것에 있어서 가장 로우 한 레벨인, 각각의 함수들이 어떻게 사용되고 어떻게 동작하는지 알고있다면 보다 쉽게 기능을 구현할 수 있다. 라는 것이다.
추후에도 새로운 기능을 구현하거나, 개선할 때.
보다 로우한 레벨, 코어한 부분에 대해 어떻게 사용되는지 한번 더 생각하고 기능을 구현해 봐야겠다.라고 생각했다.
해당 게시글에 사용한 예제는 Github에 올려두었다.
'Android > Jetpack Compose' 카테고리의 다른 글
[Android] 사용성 높은 StickyHeader 구현하기 (0) | 2024.10.24 |
---|---|
[Android] Shimmer UI 구현하기 (2) | 2024.10.06 |
[android] BottomNavigation의 구현 및 방법에 따른 차이 (1) | 2024.09.24 |
[Compose] Compose 환경에서 Drag and Drop 기능 구현해보기 - LazyColumn에서의 드래깅 (0) | 2024.08.11 |
[Compose] Compose환경 WebView에서 JavascriptInterface를 사용할 때 주의할 점 (0) | 2024.05.26 |