State in Jetpack Compose
Jetpack Compose의 상태 | Android Developers
이 Codelab에서는 상태를 관리하여 다양한 기능의 대화형 Compose 애플리케이션을 빌드하는 방법을 알아봅니다.
developer.android.com
안드로이드 Compose 상태에 관련된 코드랩을 작성하고 분석한다.
1. 실행 화면
2. 코드
@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
Column(modifier = modifier.padding(16.dp)) {
var count by remember { mutableStateOf(0) }
if (count > 0) {
var showTask by remember { mutableStateOf(true) }
if (showTask) {
WellnessTaskItem(onClose = { showTask = false }, taskName = "Have you taken your 15 minute walk today?")
}
Text(text = "You've had $count glasses.")
}
Row(Modifier.padding(top = 8.dp)) {
Button(onClick = { count++ }, enabled = count < 10) {
Text("Add one")
}
Button(onClick = { count = 0 }, Modifier.padding(start = 8.dp)) {
Text("Clear water count")
}
}
}
}
@Composable
fun WellnessTaskItem(
taskName: String,
onClose: () -> Unit,
modifier: Modifier = Modifier
) {
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
Text(
modifier = Modifier
.weight(1f)
.padding(start = 16.dp), text = taskName
)
IconButton(onClick = onClose) {
Icon(Icons.Filled.Close, contentDescription = "Close")
}
}
}
왜 'Add one' 버튼을 클릭 했을땐 TaskItem 이 다시 그려지지 못하지만 'Clear water count' 버튼을 클릭했을때 다시 그려지는 걸까 ?
이유는 showTask by remember { mutableStateOf(true) } 때문이다.
add 버튼이 눌렸을 경우 'count > 0' 조건에 성립한다면 그 하위의 count의 값이 변경되면서 WaterCounter가 재구성 되고 count가 변경된 부분에 다시 렌더링 되게 된다. 하지만 showTask는 remeber로 기존 상태 유지가 가능해 다시 렌더링 되지 않는다.
Clear 버튼이 눌렀을 경우에는 count == 0으로 변하면 count는 하위 UI트리를 호출하지 않은 상태로 리컴포지션이 발생한다. 때문에 showTask의 값은 더 이상 유효하지 않게된다. 이런 이유로 showTask의 값은 삭제되고 Add 버튼이 눌려 count > 0 의 조건이 되었을때 다시 초기화된다.
3. 스테이트풀 (Stateful) vs 스테이트리스 (Stateless)
스테이트풀(Stateful)이란?
내부적으로 상태를 가지고 있는 컴포저블을 의미한다. 스스로 내부에서 상태를 관리하고 상태가 변할 때 UI를 다시 렌더링한다. remember, rememberSaveable을 사용해 컴포저블 내부에서 mutableStateOf 같은 상태를 관리한다.
@Composable
fun Counter(){
// remember로 count 상태를 내부적으로 관리하기 때문에 Stateful 컴포저블이다.
var count by remember { mutableStateOf(0) }
Column {
Text("Count: $count")
}
}
스테이트리스(Stateless)란?
내부 상태가 없어 필요한 데이터나 상태를 외부에서 전달받아 Ui를 구성하는 컴포저블이다. 단순히 파라미터로 넘겨 받은 데이터를 기반으로 UI를 그려준다.
@Composable
fun DisplayCounter(count: Int) {
// 파라미터로 받은 count를 이용해 UI를 표기한다.
Text("count: $count")
}
컴포즈는 UI 재사용을 위해 Stateless로 컴포지션 생성을 권장한다.
@Composable
fun StatefulCounter(modifier: Modifier = Modifier) {
var waterCount by remember { mutableStateOf(0) }
var juiceCount by remember { mutableStateOf(0) }
StatelessCounter(waterCount, { waterCount++ })
StatelessCounter(juiceCount, { juiceCount++ })
}
StatefulCounter는 2개의 StatelessCounter 컴포넌트를 생성하고 있다. 스테이트리스를 이용해 컴포넌트를 구성하게 된다면 아래와 같이 juiceCount가 변경된다면 waterCount를 매개변수로 받은 컴포넌트는 재구성되지 않는다.
4. remember List vs LazyList
// remember list 표현
val list: List<String> = remember { getListTasks() }
// LazyColumn list 표현
LazyColumn{}
remember
getListTasks() List 메모리를 한번에 호출해 모두 메모리에 올린다. 컴포지션이 유지되는 동안 리컴포지션이 되지 않도록 도와준다. 하지만 리스트의 크기가 클수록 메모리 사용량이 커진다는 단점이 있다. 데이터를 UI로 보여주는 용도보다 데이터를 보관, 상태를 관리하는 용도로 사용 된다.
LazyList
LazyColumn, LazyRow 등과 함께 사용되면서 스크롤 위치에 따라 화면에 보여야하는 항목만 동적으로 렌더링한다. 전체 리스트 메모리를 한꺼번에 메모리에 올리지 않기 때문에 메모리 사용이 효율적이다.