안드로이드/compose
[Compose] 라디오 버튼 형식으로 View 출력하기
딩보
2023. 12. 7. 00:17
개발 요구사항
최근 3일 내(오늘, 내일, 모레) 식단을 식사 시간대별로 분류하여 출력
- 오늘, 내일, 모레 버튼을 라디오 버튼 형식으로 구현
- 각각의 버튼을 클릭하면 상응하는 날짜의 식단을 Text로 출력
개발 내용
- BtnDateView : 라디오 버튼을 구현한 파일
- MainActivity : 라디오 버튼의 현재값을 받아 ContentView에 전달하는 역할
- ContentView : 라디오 버튼의 현재값에 따른 출력 파일
selectedDay라는 변수 위주!
요런 느낌
DateEnum
enum class DateEnum(val date:String) {
TODAY("오늘"),
TOMORROW("내일"),
AFTER_TOMORROW("모레")
}
BtnDateView
라디오 버튼을 구현한 파일
기본 라디오 버튼은 custom하기가 어렵기 때문에 Column에 라디오 버튼 역할을 부여하였다.
object BtnDateView {
const val FONT_SIZE = 15
@Composable
fun main(selectedDay: MutableState<DateEnum>) {
RadioGroup(selectedDay)
}
}
@Composable
private fun RadioGroup(selectedDay: MutableState<DateEnum>) {
val dateList = listOf(
DateEnum.TODAY,
DateEnum.TOMORROW,
DateEnum.AFTER_TOMORROW
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 60.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
dateList.forEach { text ->
RadioBtn(
modifier = Modifier.weight(1f),
text = text.date, selectedOption = selectedDay.value,
// 현재 선택된 값으로 selectedDay의 값 변경
onOptionSelected = { selectedDay.value = it }
)
}
}
}
@Composable
private fun RadioBtn(
modifier: Modifier,
text: String,
selectedOption: DateEnum,
onOptionSelected: (DateEnum) -> Unit
) {
Column(
modifier = modifier
.selectable(
// 현재 선택되어 있는 뷰가 어떤 것인지
selected = (text == selectedOption.date),
// 뷰 클릭시, DateEnum.date 중에서 text와 일치하는 첫번째 값을 현재 라디오 선택값으로 함
onClick = {
onOptionSelected(DateEnum.values().first { it.date == text })
},
role = Role.RadioButton
),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
modifier = Modifier
.padding(bottom = 10.dp),
text = text,
color = Color.Black,
fontSize = FONT_SIZE.sp,
)
// 선택된 뷰의 텍스트 아래에 노란색 밑줄 긋기
if (selectedOption.date == text) {
Box(
modifier = Modifier
.width(40.dp)
.height(4.dp)
.background(color = colorResource(id = R.color.dark_yellow))
)
}
}
}
MainActivity
remember은 View 값이 바뀔 때 사용하는 api이다. remember을 사용하지 않고 그냥 임의로 값을 바꾸면 View의 값은 바뀌지 않으니 주의!
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
``` 메뉴를 서버에서 가져오는 과정 (본문과는 무관)
val menu = remember { mutableStateOf<MealPlan?>(null) }
lifecycleScope.launch {
menu.value = mealPlanFetcher.fetchMealPlanData()
}
```
MainView(menu.value)
}
}
@Composable
fun MainView(menu: MealPlan?) {
// selectedDay의 초기값을 오늘로 remember 변수 선언
val selectedDay = remember { mutableStateOf(DateEnum.TODAY) }
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(scrollState)
.weight(1f)
) {
BtnDateView.main(selectedDay)
ContentView.main(menu, selectedDay)
}
}
}
}
ContentView
object ContentView {
private val mealTimes = listOf("아침", "점심", "저녁")
@Composable
fun main(menu: MealPlan?, selectedDay: MutableState<DateEnum>) {
// selectedDay의 값에 따라 dayPlans에 해당하는 날짜의 메뉴 할당
val dayPlans = when (selectedDay.value) {
DateEnum.TODAY -> menu?.today
DateEnum.TOMORROW -> menu?.tomorrow
DateEnum.AFTER_TOMORROW -> menu?.theDayAfterTomorrow
}
// dayPlans의 값이 null이 아니면 해당하는 날짜의 아침, 점심, 저녁을 리스트로 형성
// null이면 null 반환
val dayPlansList = dayPlans?.let { listOf(it.morning, dayPlans.lunch, dayPlans.dinner) }
// 메뉴 출력뷰에 메뉴 띄움
if (dayPlansList != null) {
mealPlanView(dayPlansList)
}
// dayPlansList가 null이라면 메뉴 출력뷰에 "업데이트 예정"을 띄움
else {
val nullCourse = Course("업데이트 예정", null)
val mealPlansList = listOf(nullCourse)
val dayPlansList = listOf(mealPlansList, mealPlansList, mealPlansList)
mealPlanView(dayPlansList = dayPlansList)
}
}
// 메뉴 출력뷰
@Composable
private fun mealPlanView(dayPlansList: List<List<Course>>) {
BoxWithConstraints {
val screenWidth = this.maxWidth
val itemWidth = screenWidth / 2
// LazyRow를 통해 메뉴리스트 출력(recyclerview 같은 역할)
LazyRow {
itemsIndexed(dayPlansList) { index, dayPlan ->
// 아람별은 데이터 구성이 많이 중첩적이어서 한 번 더 풀어주는 과정을 거쳐야 함
// 보통은 여기서 List로 끝날테니 바로 출력하면 끝날 듯!!
DayPlanView.main(
mealTime = mealTimes[index],
courses = dayPlan,
modifier = Modifier.width(itemWidth)
)
}
}
}
}
}
결과물
전체 코드는 깃허브 참고