material calenderView를 데코레이션 해보았다.
일요일 날짜 및 텍스트 부분을 빨간색으로 나타내고
오늘 날짜를 보라색 계열로 바꾸었다.
그리고 마지막으로 날짜를 선택했을 때 뒤에 연보라색배경이 동그랗게 나타나게끔 구현해보았다.
날짜 선택 시 뒷배경이 나타나는 거는 materialCalender에서
app:mcv_selectionColor 라는 속성을 제공해주지만 뒷 배경이 너무 크게 나타나서 이것도 직접 커스텀할 필요가 있었다.
우선 materialCalender를 커스텀하려면 데코레이션 클래스를 설정해 주어야 한다.
데코레이션 클래스는 이런식으로 작성해주면 된다.
class DecorationClass:DayViewDecorator {
override fun shouldDecorate(day: CalendarDay?): Boolean {
}
override fun decorate(view: DayViewFacade?) {
}
}
그리고 적용은 이런 식으로 해주면 된다.
val decorationClass = DecorationClass()
calendarview.addDecorator(decorationClass)
먼저 일요일에 해당하는 날짜들을 빨갛게 변경시켜주는 클래스는 다음과 같이 선언하였다.
class SundayDecorator:DayViewDecorator { // 일요일 날짜 빨간색으로 설정
private val calendar = Calendar.getInstance()
override fun shouldDecorate(day: CalendarDay?): Boolean {
day?.copyTo(calendar)
val weekDay = calendar.get(Calendar.DAY_OF_WEEK)
return weekDay == Calendar.SUNDAY
}
override fun decorate(view: DayViewFacade?) {
view?.addSpan(object:ForegroundColorSpan(Color.RED){})
}
}
상단에 '일'이라고 적혀져 있는 부분도 글자색을 빨갛게 해주고 싶어서 추가로 클래스를 선언해주었다.
이거는 WeekDayFormatter를 상속으로 받는다.
class CustomWeekDayFormatter(private val context: Context) : WeekDayFormatter {
override fun format(dayOfWeek: Int): CharSequence {
val calendar = Calendar.getInstance()
calendar.set(Calendar.DAY_OF_WEEK, dayOfWeek)
val weekDayText = calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.getDefault())
val colorRes = if (dayOfWeek == Calendar.SUNDAY) Color.RED else Color.BLACK
val spannable = SpannableString(weekDayText)
spannable.setSpan(ForegroundColorSpan(colorRes), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
return spannable
}
}
그리고 적용은 위에서 설명했듯이 똑같은 방식으로 적용시켜주면 된다.
이것도 일요일 날짜 부분 색상 변경하는 로직에서 크게 차이 나지는 않는다.
class OnedayDecorator: DayViewDecorator { // 오늘 날짜 보라색으로 설정
private val calendar = Calendar.getInstance()
override fun shouldDecorate(day: CalendarDay?): Boolean {
return day?.isInRange(CalendarDay.today(), CalendarDay.today()) == true
}
override fun decorate(view: DayViewFacade?) {
view?.addSpan(ForegroundColorSpan(Color.parseColor("#7737FF")))
}
}
솔직히 이게 가장 어려웠다. 정보도 별로 없어서 방법을 찾기가 굉장히 힘들었고, 구글링과 chatgpt로 계속해서 연구한 결과 캔버스로 다 그려줘야하는 방법밖엔 없었다. (혹시 더 좋은 방법이 있다면 공유부탁 ㅎㅎ)
점 찍는 데코레이션 클래스
class CustomDotspan(radius : Float, color: Int) : LineBackgroundSpan{
private var radius = radius
private var color = color
override fun drawBackground(
canvas: Canvas,
paint: Paint,
left: Int,
right: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
lineNumber: Int
) {
val oldColor = paint.color
if (color != 0) {
paint.color = color
}
canvas.drawCircle(((left + right) / 2).toFloat(), bottom + radius + 25, radius, paint)
paint.color = oldColor
}
}
class planDotDecorator(private val context: Context, private val color: Int, private val plans: Map<String, List<TodolistData>>) : DayViewDecorator{
override fun shouldDecorate(day: CalendarDay): Boolean {
val date = formatCalendarDay(day)
return plans.containsKey(date)
}
override fun decorate(view: DayViewFacade) {
val color = ContextCompat.getColor(context, color)
view.addSpan(CustomDotspan(14f, color)) // 점의 크기와 색상 설정
val textcolor = ContextCompat.getColor(context, R.color.texthigh)
view.addSpan(object: ForegroundColorSpan(textcolor){})
}
fun formatCalendarDay(calendarDay: CalendarDay): String { // Calendarday 객체 String형으로 변환
val dateFormat = SimpleDateFormat("yyyy년 M월 d일", Locale.getDefault())
return dateFormat.format(calendarDay.date)
}
}
CustomDotspan부분이 캔버스 역할을 하는 클래스라고 보면 된다.
canvas.drawCircle(((left + right) / 2).toFloat(), bottom + radius + 25, radius, paint)
drawCircle메소드를 통해 원을 그려주었는데 왼쪽에서부터 그려줄 원의 x위치, y위치, 반지름, 마지막으로는 Paint객체인데 이게 정확히 어떤 역할을 하는지는 솔직히 잘 모르겠다.
아마 색상 같은걸 담고 있는 객체가 아닐까 추측이 된다.
다음은 날짜 및에 보라색 점을 커스텀 해주는 로직이다.
class CustomBackgroundspan(radius : Float, color: Int) : LineBackgroundSpan {
private var radius = radius
private var color = color
override fun drawBackground(
canvas: Canvas,
paint: Paint,
left: Int,
right: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
lineNumber: Int
) {
val oldColor = paint.color
if (color != 0) {
paint.color = color
}
canvas.drawCircle(((left + right) / 2).toFloat(), ((top + bottom)/2).toFloat(), radius, paint)
paint.color = oldColor
}
}
class SelectionDecortor(private val context : Context, private val color: Int) : DayViewDecorator{
private var selectedDate: CalendarDay? = null // 선택한 날짜를 저장할 변수
override fun shouldDecorate(day: CalendarDay?): Boolean {
// 선택한 날짜와 현재 날짜가 동일한지 확인
return day == selectedDate
}
override fun decorate(view: DayViewFacade?) {
val color = ContextCompat.getColor(context, color)
view?.addSpan(CustomBackgroundspan(48f, color)) // 점의 크기와 색상 설정
}
// 날짜 클릭 시 선택한 날짜 설정 메서드
fun setSelectedDate(date: CalendarDay) {
selectedDate = date
}
}
이것도 위와 같이 캔버스를 활용하여 직접 그려주었다.
사실 구현하면서 현타가 좀 오긴했다. 달력에 점하나 찍는다고, 글자 색 하나 변경한다고 이렇게 오랜시간과 긴 코드(실력 좋으신 분들은 더 간결하게 구현할 수 있겠지만 나는 아직....)가 필요한 줄은 몰랐다.
코드는 100줄 넘게 짠 것 같은데 결과는 점하나 찍히고 글자 색 바뀌는 수준이라니 정말 웃음밖에 안나온다.
이런 유용한 정보를 나눠주셔서 감사합니다.