[Android][Compose] MaterialCalendarView를 Compose에서 사용하기

2024. 5. 19. 01:23·작업 일지
반응형

기존 xml로 작업한 PWFB 프로젝트를 Compose로 변경하면서 기존에 구현했던 캘린더 UI를 어떻게 구현해야 할까 고민이 많았다. 캘린더는 MaterialCalendarView를 사용하고 있었는데, 다른 캘린더 라이브러리나 직접 구현하는 것보다는 기존 코드를 덜 수정하는 방향으로 진행하고 싶었다.

 

Compose에는 AndroidView라는 컴포저블이 있는데, Compose에서 아직 사용할 수 없는 UI 요소를 사용하는 경우에 쓰인다. AndroidView 컴포저블을 사용해서 MaterialCalendarView를 사용할 수 있었다.


AndroidView

@Composable
@UiComposable
fun <T : View> AndroidView(
    factory: (Context) -> T,
    modifier: Modifier = Modifier,
    update: (T) -> Unit = NoOpUpdate
) {
    AndroidView(
        factory = factory,
        modifier = modifier,
        update = update,
        onRelease = NoOpUpdate
    )
}

AndroindView는 매개변수로 factory, modifier, update를 받는다.

◾ factory
factory 블록은 View를 반환하는 블록을 생성하여 View 요소 또는 계층 구조를 Compose UI에 포함할 수 있게 한다.

◾ update
View가 확장되었을 때 호출되는 콜백으로 View의 정보와 상태를 업데이트를 할 수 있다.

◾ modifier
상위 Composable에서 위치를 설정하는 등의 목적으로 사용한다.

AndroidView(
    factory = { MaterialCalendarView(it) },
    modifier = Modifier,
    update = { calendarView ->
        calendarView.setHeaderTextAppearance(R.style.CalendarWidgetHeader) // 연, 월 헤더 스타일
        calendarView.setWeekDayTextAppearance(R.style.CalenderViewWeekCustomText) // 1~12월, 월간 표시
        calendarView.setDateTextAppearance(R.style.CalenderViewDateCustomText) // 일~토, 주간 표시

	// '년' '월'로 표시
        calendarView.setTitleFormatter { day ->
            val inputText = day.date
            val calendarHeaderElements = inputText.toString().split("-")
            val calendarHeaderBuilder = StringBuilder()

            calendarHeaderBuilder.append(calendarHeaderElements[0]).append("년 ")
                .append(calendarHeaderElements[1]).append("월")

            calendarHeaderBuilder.toString()
        }
		...
    }
)

 

factory에 MaterialCalendarView를 전달하고, update에서 해당 view를 사용하여 캘린더의 스타일을 변경했다.

view를 통해 대부분 기존 코드를 유지할 수 있었지만 Decorator가 필요한 수정이 필요했다.


Decorator 활용

        /**
         * themes.xml
         */
        <style name="CalenderViewCustom" parent="Theme.AppCompat">
            <item name="colorAccent">@color/c_31caab3f</item>
            <item name="android:textStyle">bold</item>
        </style>


	/**
         * activity_day.xml
         */
        <com.prolificinteractive.materialcalendarview.MaterialCalendarView
			...
            android:theme="@style/CalenderViewCustom" />

 

MaterialCalendarView에서 날짜를 클릭했을 때 서클의 색 변경은 기존 코드에선 theme를 적용하여 쉽게 적용할 수 있다.

(왼쪽)theme 적용X / (오른쪽) theme 적용O

 

 

하지만 Compose로 변경하면서 xml을 사용할 수 없기 때문에 다른 방법을 적용해야만 했다. MaterialCalendarView는 

Decorator를 사용해서 특정 날짜에 대한 커스텀 스타일을 지정할 수 있다.

 

/**
 * MainActivity
 */
val drawableList: List<Drawable?> = listOf(
    ResourcesCompat.getDrawable(
        this.resources,
        R.drawable.shape_calendar_today,
        null
    ),
    ResourcesCompat.getDrawable(
        this.resources,
        R.drawable.selector_calendar_day,
        null
    )
)

NavHost(
    navController = navController,
    startDestination = if(isFirstInit) SCREEN_NAME else SCREEN_HOME
) {
    composable(route = SCREEN_DAY) { DdayScreen(navController, drawableList) }
    ...
}

MainActivity에서 Decorator로 사용할 drawable 리소스를 List로 묶어 Compose에 전달했다. Compose에서는 drawable 리소스에 접근하지 못해서 해당 과정이 필요했다.

 

/**
 * DdayScreen
 */
@Composable
fun CalendarView(
    dDayViewModel: DdayViewModel,
    datePrefState: MutableState<String>,
    drawableList: List<Drawable?>
) {
    AndroidView(
        factory = { MaterialCalendarView(it) },
        modifier = Modifier,
        update = { calendarView ->
        
            ...
        
            val dayDisableDecorator = DayDisableDecorator(disabledDates, today, Gray.toArgb())
            val todayDecorator = drawableList[0]?.let { TodayDecorator(Yellow40.toArgb(), it) }

            calendarView.addDecorators(dayDisableDecorator, todayDecorator)

            calendarView.setOnDateChangedListener { _, date, _ ->
                calendarView.addDecorators(
                    ClearDecorator(White.toArgb(), date),
                    dayDisableDecorator,
                    todayDecorator,
                    drawableList[1]?.let { SelectDecorator(Yellow40.toArgb(), it, date) }
                )
                ...
            }
        }
    )
}
/**
 * 오늘 날짜 표시 데코
 */
class TodayDecorator(
    private var color: Int,
    private var drawable: Drawable
) :DayViewDecorator {
    private var date = CalendarDay.today()
    override fun shouldDecorate(day: CalendarDay?): Boolean {
        return day?.equals(date)!!
    }
    override fun decorate(view: DayViewFacade?) {
        view?.addSpan(object: ForegroundColorSpan(color){})
        view?.setBackgroundDrawable(drawable)
    }
}

/**
 * 날짜 선택 시 데코
 */
class SelectDecorator(
    private var color: Int,
    private var drawable: Drawable,
    private var date: CalendarDay
) : DayViewDecorator {
    override fun shouldDecorate(day: CalendarDay): Boolean {
        return day == date
    }

    override fun decorate(view: DayViewFacade) {
        view.addSpan(object: ForegroundColorSpan(color){})
        view.setSelectionDrawable(drawable)
    }
}

 

현재 날짜인 경우 setBackgroundDrawable()에 shape drawable,

날짜를 선택했을 때는 setSelectionDrawable()에 select drawable을 전달하면서 해결하였다.

 

 

반응형

'작업 일지' 카테고리의 다른 글

[Android] WebView에서 다운로드 동작이 안 되는 이유  (0) 2025.09.17
[Android] 배송 순서 최적화하기 with TSP  (2) 2025.07.01
[Android] 내비게이션의 다음 경로 정보 구하기#2  (0) 2024.03.07
[Android] 내비게이션의 다음 경로 정보 구하기#1  (5) 2024.03.05
[Android][Compose] Scaffold-topBar가 UI를 가리는 현상  (0) 2023.05.31
'작업 일지' 카테고리의 다른 글
  • [Android] WebView에서 다운로드 동작이 안 되는 이유
  • [Android] 배송 순서 최적화하기 with TSP
  • [Android] 내비게이션의 다음 경로 정보 구하기#2
  • [Android] 내비게이션의 다음 경로 정보 구하기#1
O_Gyong
O_Gyong
안드로이드 기술 정리
  • O_Gyong
    O_Gyong's TECH
    O_Gyong
    • 분류 전체보기 (81)
      • Android (58)
        • ADB (4)
        • Architecture (1)
        • Data (5)
        • Firebase (2)
        • Network & Connecting (4)
        • Security & Privacy (3)
        • UI (24)
        • 기타 (3)
        • 이슈 처리 (14)
      • Android Studio (5)
      • Git (3)
      • 작업 일지 (13)
  • 최근 글

  • 인기 글

  • 태그

    webview
    BLE
    CalendarView
    issue
    GIT
    hilt
    Room
    kizitonwose
    paging
    loading
    MQTT
    Pagination
    Android
    Android Studio
    compose
    Navigation
    TabLayout
    recyclerview
    SharedPreferences
    flow
    Andoird
    해상도
    Bluetooth
    in-app update
    CameraX
    github
    ADB
    Kotlin
    firebase
    Paging3
  • 링크

    • GitHub
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • hELLO· Designed By정상우.v4.10.4
O_Gyong
[Android][Compose] MaterialCalendarView를 Compose에서 사용하기
상단으로

티스토리툴바