자 메인 화면이다. (학점을 아름답게 볼 수 있는 화면)
메인화면 개발 당시에는 xml 내에 jetpack compose 를 어떻게 쓸 수 있는지
에 중점을 두어 개발을 했었다.
참고로 공식문서에서는 xml 과 jetpack compose 의 호환이 매우 좋다고 했었다.
몇 퍼센트였는지는 기억이 안나지만, 좋다고 했으므로 정말 그런지 한 번 확인해보자.
먼저 한 작업은 MVP 코드를 MVVM 으로 변환하는 작업이었다.
더불어 BindingAdapter 를 활용하여 총 학점에 따라 배경이나 버튼 색상이 달라지던 코드를 개선했다.
(참고로, 사진에 보이는 두 번째, 세 번째 함수는 이후에 xml 에 compose 를 넣으면서 없어졌다.)
어쨌든 당시에는 이런 식으로 리펙터링했다만, 잘 한건지는 모르겠다.
기존에 한번에 처리했던 걸 쪼개 놓았는데, 변경될 일도 없는거긴 해서 괜히했나 싶긴 하다만
오른쪽보다 왼쪽이 덕지덕지 붙어있는 느낌인건 왜일까 싶다.
어쨌든 이런 과정을 거치면서 코드 라인은 10~20줄 정도 줄였다.
일단 이름부터 바꿔야 했다.
기존엔 MainProgressBar
였는데, 통계화면에서도 쓰이기 때문에 범용적인 이름 변경이 필요했다.
그리고 기존 Java 코드를 Kotlin 으로 바꾸었고
주석 보강, 함수 및 반복적인 로직 정리 등을 통해 코드를 정리해 나갔다.
그래서 보기에는 좋아졌지만 이건 오히려 코드가 늘었다. 한 3~40줄 정도?
코드를 다시 이해하면서 주석이 없어 불편했다보니 그것들을 추가했고 그러면서 코드라인이 많이 늘은 것 같다.
예시를 들면 이렇다고 할까...?
그 외에도 애매한 함수 명을 변경하는 등 코드를 다시봐도 이해하기 위한 방향으로 리펙터링을 했다.
자 사실상 본론이다. 과연 xml 에 compose 는 잘 적용이 될까?
놀랍게도 그렇다 위의 xml 처럼 기존의 레이아웃을 ComposeView
로 처리하고
onCreate
에서 layoutId.setContent { ... }
하여
람다 내에 Composable 함수를 호출해주면 된다. 코드로 보면 이렇다.
binding.clMainInput.setContent {
MainBottomItemView(viewModel, MAIN_BOTTOM_ITEM_INPUT)
}
binding.clMainStatistic.setContent {
MainBottomItemView(viewModel, MAIN_BOTTOM_ITEM_STATISTIC)
}
MAIN_BOTTOM_ITEM_INPUT
, MAIN_BOTTOM_ITEM_STATISTIC
은 어떤 버튼인지에 대한 flag 값이다.
그리고 파란색 내의 각 버튼은 사실상 기능이 똑같다.
이렇게 2개 기능밖에 없기에 이 기능들을 통합했다.
그래서 기존에 리펙터링하면서 각 버튼의 bindingAdapter 로직을 Composable 함수에 넣었다.
그리고 동일한 기능이라는 건 통합도 가능하다는 말이다.
위의 setContent 코드에서 예상한 분들도 있었겠지만
Composable 함수의 두번째 파라미터 값이 각각 어떤 이미지, 클릭 이벤트를 쓸 것인지를 분기한다.
val title = arrayOf(
stringResource(id = R.string.tv_main_input),
stringResource(id = R.string.tv_main_statistic)
)[position]
val drawables = arrayOf(
listOf(
R.drawable.m_input_a,
R.drawable.m_input_b,
R.drawable.m_input_c,
R.drawable.iv_main_input
),
listOf(
R.drawable.m_statistic_a,
R.drawable.m_statistic_b,
R.drawable.m_statistic_c,
R.drawable.iv_main_statistic
)
)[position]
val clickEvent = viewModel?.let {
arrayOf(
it::clickInputLayout,
it::clickStatisticLayout,
)[position]
} ?: { Timber.i("for Preview") }
이런 식으로 처리했으며 bindingAdapter 에서 중복 코드가 있어 아쉬웠던 내용을 이런 방식으로 개선했다.
val mainScore = viewModel?.mainScore?.observeAsState() ?: remember { mutableStateOf(0f) }
Column(
// 1
modifier = Modifier.clickable(
enabled = true,
onClick = clickEvent,
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() }
),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
painter = painterResource(
id = mainScore.value?.let {
// 2
when {
it >= 4 -> drawables[0]
it >= 3 && it < 4 -> drawables[1]
it >= 2 && it < 3 -> drawables[2]
else -> drawables[3]
}
} ?: drawables[3]
),
contentDescription = title
)
Spacer(modifier = Modifier.height(32.dp))
Text(
text = title,
fontSize = 15.sp,
color = colorResource(id = R.color.defaultTextColor),
textAlign = TextAlign.Center
)
}
실제 View 는 이렇다.
생각보다 적을 내용이 없을 정도로 간단했다.
우리는 앞으로 ComposeView 에 setContent 를 설정하여 Composable 함수를 호출해주면
xml 내에 녹아드는 Compose 를 볼 수 있을 것이다.
여기까지는 되게 간단했었다. 하지만... 다음장 (학점 입력 화면) 에서 정말 한 것이 많았다.
commit 기록을 돌아보아도 여기 리펙터링 과정이 한 2주정도 걸린 것 같다.
당연히 온전히 100% 2주는 아니고, 짜투리 시간에서 2주이지만 그래도 다른 작업에 비해 오래 걸렸다.
많은 작업이 있었고 다음 포스팅에서 요 내용들을 다뤄보려 한다.