만드는 과정

2. 달력 앱 만들기 시작 - 기본 형태 만들기

그저 달력 2025. 1. 16. 13:50

기본적인 달력 형태를 만들기 시도해봤다.

 

한 화면에 한달이 보이고,

좌우로 스크롤 하면 전달, 다음달이 나오도록 

만들어보는 걸 목표로 했다.

 

화면 하단에는 공간을 남겨두고

나중에 일정 데이터의 입출력이 가능해지게 될 때,

"오늘의 일정"을 보여주면 좋겠다고 생각했다.

 

구글을 검색해서 적당한 코드를 찾기 시작했다.

 

내 의도에 딱 맞는 코드를 발견했다..

 

참고 코드

 

Android Kotlin Custom Calendar - 커스텀 달력

1. 결 과 # 이 글은 RecyclerView를 이중으로 사용하여 커스텀 달력을 만드는 방법을 기술한다. 우선 가로형 타입의 RecyclerView 사용하여 각 월을 나타내며 이 안에서 각 일수를 나타내기 위한 Grid 타입

notepad96.tistory.com

 

기본적인 구조는 이렇다.

 

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