기본적인 달력 형태를 만들기 시도해봤다.
한 화면에 한달이 보이고,
좌우로 스크롤 하면 전달, 다음달이 나오도록
만들어보는 걸 목표로 했다.
화면 하단에는 공간을 남겨두고
나중에 일정 데이터의 입출력이 가능해지게 될 때,
"오늘의 일정"을 보여주면 좋겠다고 생각했다.
구글을 검색해서 적당한 코드를 찾기 시작했다.
내 의도에 딱 맞는 코드를 발견했다..
기본적인 구조는 이렇다.
1. 앱이 처음 실행될 때 나오는 화면(mainactivity)에,
2. Recyclerview*(A라고 하자)를 넣는다.
* 한 화면에 하나의 item이 표시되고, 좌우로 스크롤 되는 Recyclerview
* 이 Recyclerview의 item이 각각 한달이 된다.
3. A 안에 Recyclerview (B라고 하자)*를 다시 넣는다.
* A 안에 42개(6행 7열)의 item이 표시되는 recyclerveiw
* 이 Recyclerview의 item이 각각 하루가 된다.
※ Recyclerview는 틀(viewholder)을 최소한으로 만들고, 이걸 계속 재활용해서 쓰는 방식의 view이다.
동일한 틀에 데이터만 바꿔주는거다.
이렇게 하면 시스템 자원을 효율적으로 사용할 수 있게 된다.
재활용 되는 방식(틀을 위아래로 묶을지, 좌우로 묶을지, 격자로 묶을지)은 설정 가능하다.
묶여진 각각의 틀에는 position이라는 번호가 부여되면서 각각 구별할 수 있게 된다.
틀 묶음에서 제일 첫번째 틀의 position은 0이고, 그뒤부터 1씩 증가한다.
우리 앱에서는 A는 좌우로, B는 격자로 묶어서 틀이 재활용되고 있다.
4. A에 들어갈 연월을 생성하는 코드를 살펴보자.
먼저, 앱을 처음 실행할 때 화면에 표시되는 동작을 구현하는 MainActivity.kt 파일에서,
화면에 처음으로 보여지는 A의 position을
scrollToPosition(Int.MAX_VALUE/2)로 설정해준다.
(MAX_VALUE는 java에서 Int 타입의 최대값(2,147,483,647)으로,
그 절반인 1,073,741,823이 앱 실행 시 화면에 처음으로
보여지는 position이 된다.)
그리고, A의 동작을 구현하는 adapterMonth.kt라는 코드에 보면,
코드의 초반부에,
val center = Int.MAX_VALUE / 2 이렇게 설정되어 있고,
(A가 화면에 처음 보여질때의 position과 동일)
onBindViewHolder 블럭 내부에 이런 부분이 있는데,
1) calendar.time = Date()
2) calendar.set(Calendar.DAY_OF_MONTH, 1)
3) calendar.add(Calendar.MONTH, position - center)
이게 뭐냐면,
1) 오늘 날짜를 생성(2025.1.16.)하고,
2) 1)을 그 달의 1일로 세팅(2025.1.1.)해서,
3) 스크롤 할 때 마다, 변경되는 position에 따라 월을 변경해주는 거다.
(앱을 처음 실행 할 때는 position - center가 0이니까 첫 세팅(2025.1.1.) 그대로이고,
오늘쪽으로 스크롤해서 position이 1 증가하면, position - center이 1이 되니까
다음달(2025.2.1.)이 되는거다)
이렇게 A를 스크롤해서 position이 변경될 때, 월이 변경되게 해주고 있다.
그러니까, 앱을 실행하고, mainactivity에서 A를 화면에서 처음 보여줄 때,
A의 position인 Int.MAX_VALUE/2(1,073,741,823)에서, position - center=0에 따라
항상 앱을 실행했을 때의 해당 월을 표시해주게 된다.
그리고, 좌우로 스크롤할 때 마다 position이 변경되면서, 전달, 다음달로 바뀌게 된다.
5. B에 들어갈 각 월의 날짜를 생성하는 코드를 살펴보자.
A의 동작을 구현하는 adapterMonth.kt라는 코드에 보면,
onBindViewHolder 블럭 내부에 이런 부분이 있는데,
1) var dayList: MutableList<Date> = MutableList(6 * 7) { Date() }
2) for(i in 0..5) {
for(k in 0..6) {
calendar.add(Calendar.DAY_OF_MONTH, (1-calendar.get(Calendar.DAY_OF_WEEK)) + k)
dayList[i * 7 + k] = calendar.time
}
calendar.add(Calendar.WEEK_OF_MONTH, 1)
}
이게 뭐냐면,
1) 날짜 42개가 들어갈 행 6개, 열 7개짜리 리스트를 만들어 놓는다.
2) i가 0~5까지(6행의 주를 생성하기 위해) 늘어나는동안,
각각 k가 0~6까지(요일별 날짜 생성을 위해) 늘어나면서 날짜를 생성해준다.
현재 각월의 1일로 세팅이 되어 있으니까(4. 월 생성과정에서),
i가 0일 때, k가 0~6까지 순차적으로 증가하면서,
(1-calendar.get(Calendar.DAY_OF_WEEK)) + k) 이 코드를 통해
1일이 있는 주의 일요일부터 토요일까지 날짜를 생성해주게 되고,
i가 늘어나면, 그 다음주부터 순차적으로 요일별 날짜가 생성된다.
이렇게, i가 5가 될 때까지 6주간 각 주별로 7일의 날짜가 생성된다.
생성된 날짜들은, dayList에 저장된다..
최종적으로, 생성된 날짜가 저장된 dayList는
B의 동작을 구현하는 adapterDay.kt로 넘겨지고, B에서 각각 표시된다.
뷰바인딩을 사용하도록, 코드를 좀 수정하는 사소한 작업이
필요했지만, 이렇게 일단은 기본적인 형태의 달력을 구현해볼 수 있었다.
'만드는 과정' 카테고리의 다른 글
1. 앱을 만들기 위한 준비 (0) | 2025.01.15 |
---|