오랜만에 포스팅을 올려본다.
요즘 현생을 사느라 많이 바빴었기에, 이제야 Planner 개발 기록을 올리게 되었다.
또한 기존 checkBox 리스트의 문제를 해결하느라 더 오래 걸렸던 것 같다 >:(
이번에는 주간 통계표를 구현하면서 있었던 일들에 대해서 설명해 주겠다.
1. 계획
주간 통계 리스트를 만들기 위해 기존에 구현한 로직을 테스트하던 중, 나는 문제 하나를 발견했다.
Planner의 일정을 보여주는 로직은 날짜를 클릭해서(날짜가 변함에 따라서) 일정의 정보가 바뀌어야 하는 로직으로 구현하였는데, 날짜를 클릭해서 날짜를 바꾸면 기존의 checkbox state가 변하지 않고 바뀌기 전 것으로 계속 남아있는 문제를 발견했다.
위와 같이 기존에 선택된 일정 정보(11월 27일의 가장 위의 2개의 일정이 true임)가 새로 선택된 일정 정보(28일의 가장 위의 2개 일정은 false임) state를 제대로 받아오지 않던 문제가 있었다.
처음에는 특정한 item들이 갱신되지 않아서 발생하는 문제라는 것을 알고 compose recomposition 문제인 줄 알았다.
그래서 처음에 저 이슈를 마주했을 때 필자는 compose recomposition(재구성)을 최적화하지 않아서 문제가 생긴 줄 알고 그쪽으로 삽질해 보다가 기존 checkbox를 구성하는 state가 문제라는 것을 아는 데에는 2일이 걸렸다 :(
그래서 필자는 dynamic list에서 checkbox state를 사용하는 방법에 대해서 찾아보기(삽질) 시작했다.
2. CheckBox state 변경하기
3일에 걸쳐서 삽질 후 해결법을 찾았다.
기존의 checkbox state 코드는 다음과 같았다.
@Composable
fun ScheduleItem(
planData: PlanData,
checked: Boolean,
onCheckBoxClick: (Boolean) -> Unit
) {
var checkState by remember { mutableStateOf(checked) }
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
modifier = Modifier.padding(vertical = 7.dp, horizontal = 10.dp),
checked = checkState,
onCheckedChange = {
checkState = !checkState
onCheckBoxClick(checkState)
}
)
. . .
}
}
item composable 안에서 따로 checkbox state를 생성해서 적용해 주었다.
위와 같이 만들어서 문제가 발생하였다.
필자는 일단 PlanData부터 바꾸어 주었다.
기존에 boolean tpye이었던 complete를 state로 생성해 주었다.
class PlanData(
val title: String = "",
val description: String = "",
complete: Boolean = false
) {
var complete by mutableStateOf(complete)
}
class 파라미터로 받아온 complete를 mutableState complete의 초기값으로 사용하게 했다.
다음으로 기존에 문제가 있던 ScheduleItem을 수정해 주었다.
@Composable
fun ScheduleItem(
planData: PlanData,
onCheckBoxClick: (Boolean) -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
modifier = Modifier.padding(vertical = 7.dp, horizontal = 10.dp),
checked = planData.complete,
onCheckedChange = {
onCheckBoxClick(!planData.complete)
}
)
. . .
}
}
새로운 코드에서의 planData.complete는 그냥 boolean이 아닌, boolean state로 변경된다.
그러면 따로 composable에서 state를 생성해 줄 필요 없이 state를 사용할 수 있다.
3. ViewModel 로직 추가하기
기존의 PlanScreen의 비즈니스 로직을 구현한 ViewModel에 새로운 함수를 생성해 준다.
private val _plans = mutableStateListOf<PlanData>()
val plans: List<PlanData> get() = _plans
fun changePlanCompleteAtIndex(position: Int, checked: Boolean) {
_plans.find { _plans.indexOf(it) == position }?.let { data ->
data.complete = checked
}
}
일정 정보를 받아올 수 있는 mutableState를 생성해 주고,
changePlanCompleteAtIndex()를 만들어서 _plans에서 it의 index를 구해서 position 파라미터와 같은 데이터의 complete를 변경해 주는 로직으로 구성되는 함수이다.
마지막으로 생성한 함수를 적용해 주겠다.
itemsIndexed(planState) { position, it ->
ScheduleItem(
planData = it,
onCheckBoxClick = { isCheck ->
planViewModel.changePlanCompleteAtIndex(position, isCheck)
planViewModel.planCheck(
uid = uid!!,
date = dateState.value!!,
position = position.toString()
)
statisticsViewModel.modifyData(
uid = uid,
isCheck = isCheck,
mode = StatisticsMode.COMPLETED
)
}
)
. . .
}
기존의 checkbox 클릭 이벤트에 위에 새로 만든 함수를 적용해 주면 끝이다.
그러면 실행 결과를 확인해 보고 마무리 짓겠다.
4. 실행 결과
체크박스도 정상적으로 작동하고, 날짜를 변경해도 기존의 checkbox state가 유지되는 것을 볼 수 있다.
정리
- checkbox list에 대한 예제는 많이 나와있었지만 동적 리스트에 대한 자료는 많이 나와있지 않았었다. 그래서 자료 찾기가 힘들었다.
- 이번에 checkBox 이슈를 해결하면서 compose의 최적화(recomposition 관리)와, compose의 state에 대해 깊게 공부해 볼 수 있었던 시간이었다.
참고 자료
https://developer.android.com/codelabs/jetpack-compose-state
'⛏️ | 개발 기록 > ⏰ | Schedule Planner' 카테고리의 다른 글
[Android, Kotlin] 통계 화면 구성하기 (14) (0) | 2024.04.14 |
---|---|
[Android, Kotlin] firebase 데이터베이스 변경하기 (13) (0) | 2024.04.10 |
[Android, Kotlin] Compose에서 DataStore 사용하기 (11) (0) | 2023.11.19 |
[Android, Kotlin] Firebase RTDB에서 데이터 순차적으로 삽입하기 (10) (0) | 2023.11.12 |
[Android, Kotlin] Android 12 버전 이상에서 Compose로 Custom Splash Screen 생성하기 (9) (0) | 2023.11.10 |