/**
* 공통으로 사용하는 아이템
*/
@Composable
fun ListItem(
alpha: Float,
modifier: Modifier = Modifier
) {
Text(
text = "List item",
modifier = modifier
.padding(32.dp)
.graphicsLayer {
this.alpha = alpha
}
)
}
투명도 변화에 alpha 값을 사용하는 공통 Composable ListItem이 있다고 가정하자.
// BAD
@Composable
fun ListScreen() {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
) {
for(i in 1..50) {
ListItem(
alpha = scrollState.value / 50f,
modifier = Modifier.fillMaxWidth()
)
}
}
}
scrollState가 변화하게 된다.
Float 값으로 ListItem에 직접 전달ListItem은 투명도만 변화하는데도 불구하고 Recompostion 됨.// GOOD
@Composable
fun ListScreen() {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
) {
for(i in 1..50) {
ListItem(
**alpha = { scrollState.value / 50f }**, // <- 차이점
modifier = Modifier.fillMaxWidth()
)
}
}
}
...
@Composable
fun ListItem(alpha: () -> Float, modifier: Modifier = Modifier) { // 타입이 () -> Float
Text(
// ...
.graphicsLayer {
this.alpha = alpha() // 람다를 호출하여 최신 값을 얻음
}
)
}
ListItem에 전달ListScreen이 재구성될 때, alpha = { scrollState.value / 50f } 라는 람다는 스크롤 상태가 변하더라도 객체 참조 자체는 동일함.ListItem 컴포저블은 인자로 동일한 람다 객체를 받기 때문에, Compose는 ListItem을 Recomposition할 필요가 없다고 판단하고 건너뜀.ListItem 내의 **graphicsLayer**는 레이아웃/그리기 단계에서 실행됨.
alpha()를 호출하여 스크롤의 최신 값을 즉시 가져옴.scrollState가 수시로 변해도 ListItem은 Recompostion을 건너뛰고 오직 레이아웃/그리기
단계에서만 graphicsLayer의 속성을 업데이트하여 성능을 최적화함.