본문 바로가기

Android/Java

Material Calendar 커스텀하기 (1)

반응형


18.11.28 수정

----------------------


오늘은 지금 하고 있는 프로젝트에서 골머리를 썩히고 있는!!!


Material calendar 에 대해 써보려고 합니다.


많이 부족하고 이게 맞나 싶지만... 열심히 노력하고 있습니다!!!


시작하겠습니다 ㅎㅎ




제가 사용한 캘린더는 


1. https://github.com/prolificinteractive/material-calendarview


2. https://github.com/Applandeo/Material-Calendar-View


위의 두개를 사용했습니다.




우선 여기서는 1번만 다룰 예정입니다.


추후 2번을 업데이트 하겠습니다 ㅎㅎ 



저는 1번을 월별 달력과 주별을 사용하려 했지만... 월별만 사용했고 dot를 여러 개 찍는 걸로 했습니다.


dot를 여러 개 찍는 법은 나와있지 않아 많이 고생했던....흑흑


제가 진행했던 것은!!!




1. 라이브러리를 gradle이 아닌 import module을 통해 가져온다.


2. 나의 입맛에 맞게 커스텀한다.




입니다!! 




커스텀을 하려고 하니 많이 힘들더라구요... 남이 짜놓은거라 참 해석하는데도 오래 걸리고


타고 들어가는것도 한 두 뎁스지... ㅎㅎ 그래도 많이 배우고 있습니다.




import 하는 법부터 간단한 커스텀까지 보여드릴게요!




1. import 하는 방법


(1) 깃헙에 들어가 .zip 파일을 받는다.


(2) ,zip 파일을 풀고 library만 남겨둔다.


(3) 안드로이드 스튜디오에서 file -> new -> import module -> 해당 library가 들어있는 주소까지 가서 library를 클릭한다.


(4) library를 클릭하고 확인하면 Module name 을 적으라는 칸이 뜨는데, 여기선 자기가 원하는 library 이름를 적습니다.


(5) finish를 누르면 안드로이드 스튜디오의 해당 경로로 들어가면 library가 가져온 것을 볼 수 있습니다.


(6) 다시 한 번 library를 (3)~(4) 까지 반복합니다. (여기서는 해당 파일 안에 library를 가져와야 합니다. (3)의 경로가 아닙니다.)


(7) 다시 가져오게 된 후 똑같이 이름을 적는 칸이 생기면 library이름을 다시 적습니다.(아마 3~5번을 진행 안하고 그냥 끌어다 놓고 진행해도 될 듯 하네요...)


(8) 라이브러리가 안드로이드 스튜디오 안에 파일형식으로 들어온 것을 볼 수 있습니다!!!


끝!!!!

이 아닙니다.

라이브러리를 가져왔으면, 사용할 수 있게끔 만들어줘야 합니다!!!


(9) file -> project structure -> app -> Dependencies -> (+) 클릭 -> 3번의 Module dependency 클릭 -> 라이브러리 클릭 -> ok -> ok


이제 진짜 끝!!!


이렇게 하면 라이브러리를 가져다 쓸 수 있습니다. 참 쉽죠? (전 고생했습니다 첨해봐서...ㅠㅠ)


이렇게 하면 몇개의 라이브러리를 계속 가져다 쓸 수 있습니다.




2. 사용 방법


이제 캘린더를 가져왔으니 사용해 주어야겠죠?


이제 xml로 들어갑니다.


그리고 아래의 코드를 구성합니다.



<com.prolificinteractive.materialcalendarview.MaterialCalendarView
android:id="@+id/main_calendar_mcv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:mcv_selectionColor="@color/black" />



이렇게 구성하면 사용할 준비 끝!!!


저는 id 값을 줄 때, 해당 activity name을 같이 넣어줍니다.. 안 헷갈리게요..


app:mcv_selectionColor 는 선택 시의 "원의 색"을 나타냅니다. 자세한건 위의 깃헙 주소로 들어가면 다 나온답니다.




이렇게 구성해놓았다면, 이제 코드를 작성합니다.


이제 달력을 가져다 쓸건데!!! 달력은 뭐가 필수죠?




1. 달력에 표시되는 dot!


2. 달력을 클릭할 때 나타나는 이벤트!!


3. 달력을 좌/우 로 스와이프하는 이벤트!!!


4. 원하는 값을 가져오는!!!!




우선 이렇게만 되도 성공입니다.


이 캘린더는 초기값을 설정해주어야 합니다.


onCreate가 될 때 아래와 같이 선언해주는게 좋습니다!


materialCalendarView.setOnDateChangedListener(this);
materialCalendarView.setOnMonthChangedListener(this);
materialCalendarView.setTopbarVisible(false);

int beforeYear = Integer.parseInt(getYear(String.valueOf(beforeMonth().getTime().getYear())));
int afterYear = Integer.parseInt(getYear(String.valueOf(afterMonth().getTime().getYear())));

materialCalendarView.setSelectedDate(calendarDayToday);
materialCalendarView.state().edit()
.isCacheCalendarPositionEnabled(false)
.setFirstDayOfWeek(DayOfWeek.SUNDAY)
.setMinimumDate(CalendarDay.from(beforeYear, beforeMonth().getTime().getMonth()+1, beforeMonth().getTime().getDate()))
.setMaximumDate(CalendarDay.from(afterYear, afterMonth().getTime().getMonth()+1, afterMonth().getTime().getDate()))
.setCalendarDisplayMode(CalendarMode.MONTHS)
.commit();

materialCalendarView.setShowOtherDates(MaterialCalendarView.SHOW_OUT_OF_RANGE);
materialCalendarView.setDynamicHeightEnabled(true);
materialCalendarView.setPadding(0, -20, 0, 30);
materialCalendarView.setWeekDayTextAppearance(R.style.CustomTextAppearanceWeek);
materialCalendarView.setDateTextAppearance(R.style.CustomTextAppearanceDayClicked);


1 번 줄은 날짜 클릭 시 이벤트

2번 줄은 달력이 변화할 때의 이벤트.

3번 줄은 Topbar 를 보여줄건지에 대한 것입니다.


저는 보여주지 않기로 했습니다. 왜냐하면 굳이 보여줄 필요가 없고 toolbar를 사용해 구성할거기 때문이었죠...(보여줘도 상관 없습니다.)


5~6. 제가 보여주고 싶은 날짜의 연도들을 계산한 겁니다. 예를 들어, 현재 날짜의 앞 뒤로 60일치만 보여줄 것이다. 라고 했을 때, 

before은 18년(현재 11월 28일로 가정), after는 19년이 됩니다.


8번 줄은 현재 날짜를 selecte 시킨 것입니다.


9~15 가장 중요합니다!!! 별표 4.5개


state().edit()를 통해 뭘 어떻게 보여줄 것이다를 commit()하기 위한!! 것이죠(라이브러리 내의 소스로 따라가다보면 이해가 갈 거에요)


10번 줄은 캐쉬를 지정 안한다는 말일거고...(이건 뭔지 모르겠어요 ㅠㅠ 설명 참고하세요)

11 번 줄을 통해 일요일부터 토요일 순으로 한다고 지정을 했고

12번 줄을 통해 달력의 최소값을,

13번 줄을 통해 달력의 최대값을,

14번 줄을 통해 "월"로 달력을 표시되게 구성했습니다.

15번 줄을 통해 위의 코드가 작동하게 commit 했습니다.


이렇게만 해도 달력 사용은 가능해집니다.


16번 줄은 해당 월의 최소값 이전의 날들, 최대값 이후의 날을 끝까지 보여준 다는 것입니다. 

예를 들어, 최소값이 10월 15일이면, 1~14일은 표시되지 않지만, 위와 같이 설정해주면 나머지 날들이 보이게 됩니다. 단 클릭은 불가능합니다.

따로 설정하셔야해요!!! 이부분은 씨크릿...!!! 궁금하시면 알려는 드릴게...요..


17 번 줄은 달력이 4주만 있는게 아니죠? 5주도, 6주도 가능합니다!! 다들 아시죠?

그렇게 때문에 그 높이를 dynamic하게 보여줄 것이다 라는 뜻입니다.


18번 줄은 각 일들의 패딩값을 강제할당 한건데 잘 모르겠네요..


19번 줄은 요일에 대한 text 를 커스텀 한 것입니다. 입맛에 맞게 커스텀해보세요! (이거 찾느라 죽을뻔했어요 저는..) 별표 3.5개


20번 줄은 "일"에 대한 text를 커스텀 한 것입니다. 이것도 중요도 별표 3.5개!!!


여기까지는 간단하지만, 저는 찾는데 어려움을 겪었습니다




이 캘린더는 .addDecorator를 통해 다양한 데코를 지정할 수 있습니다.


깃헙에 자세히 나와있으니 참고하세요!! 어려운 영어는 없어요 ㅎㅎ




자 이제 원하는 날짜를 클릭 시 이벤트를 지정해봅시다!!!


우선 위의 1번 줄을 잘 쓰셨다면, 2번째로는 


OnDateSelectedListener


를 상속받읍시다.


그러면 import 하라고 빨간줄이 뜨는데 추가해주면 아래와 같은 코드가 나옵니다.


@Override
public void onDateSelected(@NonNull MaterialCalendarView widget,
@NonNull CalendarDay date,
boolean selected) {


}


여기서 원하는 코드를 지정해주면 됩니다.


저는 설정해 놓은 "오늘"을 표시해주는 decorator를 지운다던가, 필요없는 selector를 지웠습니다.


그건 자유입니다(저도 아직 헷갈리기 때문이죠...)


date.getTime()을 통해 선택한 날을 가져올 수 있습니다.






이번엔 달력이 스와이프 되는 것을 봅시다.


2번 줄을 쓴 뒤,  


OnMonthChangedListener


를 상속받읍시다!!!


그러면 아래와 같은 코드가 override될 것입니다.


@Override
public void onMonthChanged(MaterialCalendarView widget, CalendarDay date) {


}


짠!! 저는 여기서 material calendar 의 Topbar를 쓰지 않았기 때문에 따로 구현한


toolbar 안에 text를 구현해 거기다 스와이프 될때마다 바뀌는 "월"을 표시했습니다.


date.getDate()를 통해 가져온 값을 string으로 변환해 


현재 날짜(CalendarDay calendarDay = CalendarDay.today() 를 통해 가져올 수 있습니다.)와 비교해 


2018년이 같으면 월만, 2018년이 아니면 해당 년과 월을 text 에 표시해줬습니다.


참 쉽죠? 






이제 기본적인 세팅은 끝났습니다.


이제 decorator를 만들어봅시다!!




제가 진행한건 가장 간단한 오늘 날짜 deco와

해당 일에 이벤트가 있다면 표시될 dots deco 

다른 날 클릭 시 deco




이렇게 3개를 구성했습니다.


1, 3번은 깃헙에 있으니 확인하세요!




2번째꺼는 좀 오래 찾았던거 같네요 원하는게 없어서...ㅠㅠ


우선 해당 이벤트를 "표시"할 decorator를 구성합니다.


코드는 아래와 같습니다.



class EventDecorator(private val context: Context,
private val stringProductColor: Array<String>,
private val dates: CalendarDay) : DayViewDecorator {

private lateinit var colors: IntArray

override fun shouldDecorate(day: CalendarDay): Boolean = dates == day

override fun decorate(view: DayViewFacade) {


...


colors = IntArray(stringProductColor2.size)
for(i in stringProductColor2.indices) {
colors[i] = Color.parseColor(stringProductColor2[i])
}

view.addSpan(CustomMultipleDotSpan(5f, colors))

}
}




(전에 짰던 코드들은 자바고 현재 짜고 있는 코드는 코틀린이라 헷갈리실겁니다.... 저도 헷갈리니까요...)


우선 표시할 색들을 array형태로 string값들("#ff0000" << 이런 형식, 아 친절하다 난 고생했는...) 받아오고, 


Color.parseColor로 intArray로 변경해줍니다.


이를 따로 구성한 CustomMultipleDotSpan 안에 넣습니다!!


앞에 5f는 dot의 size 입니다.




기다리고기다리던 여러 개의 dots 찍는 코드... 뜨뜬... 엄청 고생했다는... 소문이...


아래와 같습니다.


class CustomMultipleDotSpan : LineBackgroundSpan {
private val radius: Float
private var color = IntArray(0)

constructor() {
this.radius = DEFAULT_RADIUS
this.color[0] = 0
}

constructor(color: Int) {
this.radius = DEFAULT_RADIUS
this.color[0] = 0
}

constructor(radius: Float) {
this.radius = radius
this.color[0] = 0
}

constructor(radius: Float, color: IntArray) {
this.radius = radius
this.color = color
}

override fun drawBackground(canvas: Canvas, paint: Paint,
left: Int, right: Int, top: Int, baseline: Int, bottom: Int,
charSequence: CharSequence,
start: Int, end: Int, lineNum: Int) {

val total = if (color.size > 2) 3 else color.size
var leftMost = (total - 1) * -12

for (i in 0 until total) {
val oldColor = paint.color
if (color[i] != 0) {
paint.color = color[i]
}
canvas.drawCircle(((left + right) / 2 - leftMost).toFloat(), bottom + radius, radius, paint)
paint.color = oldColor
leftMost += 24
}
}
}




복잡해 보이지 않죠? 쉬워요!!!! 결국엔


canvas.drawCircle 에서 원을 "그려" 주어 나타내는 것입니다.


그렇기 때문에 "해상도"에 따라 크기가 다를 수 있는 것을 참고하시기 바랍니다.(이 부분은 해상도의 x축 사이즈를 받아와 구성할 수 있습니다)


이렇게 만들어 놓은 eventDecorator를 이제 materialCalenar 에 .addDecorator(deco) 해서 넣어주면 됩니다!!


leftMost는 점과 점 사이의 거리를 나타냅니다.(수많은 테스트를 통해 확인했어요...)




그러면 원하던 여러 개의 dots 들을 나타낼 수 있습니다.




이제 끝!!! 이 아니라 하나만 더!!




저는 클릭 시 나타나는 원의 크기가 너무 커서 싫더라구요..


찾아보니까 역시 !!!! 이것도 그려주고 있더라구요...(다 그려 이놈은...)


어디에 있냐!!




찾다 찾다 발견한 DayView.java << 요놈 안에 있습니다.





요놈으로 들어가서 맨 밑에 가보면, circleDrawableRect 에 좌, 우, 상, 하 높이를 지정해 그려주고 있습니다.


이 부분을 잘 생각해 변경하시면 됩니다.


tempRect 는 월에서 따로 "일"을 클릭하는 것이 아닌, set시키는 곳에 나타낼 "원" 에 대한 "임시 dot" 이라 생각하시면 됩니다.


즉, 둘다 수정해야 한다는 거죠...


이렇게 하면 "원"의 크기도 줄일 수 있습니다.








이외에도 몇 가지 수정했는데 기억이 잘 안나네요... 여튼 힘내시고 궁금한 부분은 댓글 달아주면 


아는 범위 내에서


가르쳐드립니다.




이상 1번 캘린더 커스텀 방법이었습니다. 

반응형