본문 바로가기
앱인벤터/강좌

앱인벤터 강좌 #6 달력 만들기

by 이지이지(EGEasy) 2022. 8. 30.

앱인벤터로 구현한 달력


 

안녕하세요. 이지이지입니다.

이번 앱인벤터 강좌에서는 달력 만들기에 대해 설명해보도록 하겠습니다.

 

달력 만들기 알고리즘은 고려할 사항이 많습니다.

선택한 년도의 달에 1일이 무슨 요일에 시작하는지에 따라 달력의 형태가 바뀌기 때문이죠.

 

먼저, 예제에 사용된 소스를 내려받아 함께 보면서, 아래 설명을 읽어보도록 하세요.

 

EG_Calendar.aia
0.19MB

 

    화면 구성

본 예제의 화면 구성은 다음 이미지와 같습니다.

 

달력 화면 구성

 

 

Non-visible components에 대해 먼저 설명하자면

1. MakeViewUp1: 눈에 보이는 컴포넌트(ex. 버튼, 레이블, 텍스트 상자 등)에 디자인적인 요소를 더해주는 확장 프로그램입니다. 본 예제에서는 오늘 날짜를 둥근 파란색 배경으로 설정하기 위해 사용하였습니다. 

2. Clock1: 연도, 월, 일을 가져오기 위한 시계 컴포넌트입니다.

 

 

다음으로 본 강좌의 핵심인 달력을 나타내고 있는 부분을 그림으로 표현해 본 것입니다.

달력의 화면 구성

 

달력의 날짜를 표시하기 위해 버튼 컴포넌트를 사용하였고, 각 주별로 7개씩의 버튼이 들어 있습니다.

총 6주를 표시할 수 있게 하여, 총 42개의 버튼을 사용하였습니다.

 

총 6주, 42개의 버튼을 사용한 이유는 다음과 같습니다.

 

최소, 최대 달력

 

위 이미지처럼 1일이 일요일이고, 2월 28일까지 있다면 4개주만 필요하고,

1일이 토요일이고, 31일까지 있는 달이라면 6개주가 필요합니다.

 

그래서 최대 6주, 42개의 버튼(사실 최대 37개의 버튼이면 됩니다만, 그냥 통일성 있게 42개로...)을 만들어 두고,

만약 6주 또는 5주에 표시할 내용이 없다면, 표시할 내용이 없는 주는 보이지 않게 할 것입니다.

 

날짜를 나타내기 위해 레이블 컴포넌트를 사용하지 않고, 버튼 컴포넌트를 사용한 이유는

보통 달력의 경우, 달력 자체만 사용하기 보다는 일정 입력 등 다른 이유로 사용하는 경우가 많아, 클릭 이벤트를 사용할 수 있게 하기 위해서입니다. 

 

 

    블록 코딩

본 강좌에 사용된 소스의 전체 블록은 다음과 같습니다.

 

블록 코딩

 

 

보시다시피 굉장히 복잡합니다.

최대한 자세히 알고리즘 순서대로 설명드리도록 하겠습니다.

 

 


먼저 본 예제에 사용된 변수들에 대한 블록입니다.

cf. 변수는 앱이 처음 실행될 때, 초기화(처음 설정한 값) 됩니다.

 

변수 블록

 

 

① months 변수에 각 달의 명칭을 리스트 형태로 담아둡니다.

- list from csv row text는 콤마(,)로 구분되어 있는 텍스트를 리스트 형태로 만드는 명령입니다.

 

② months30 변수에 날짜가 30일까지 있는 달을 리스트 형태로 담아둡니다.

 

③ months31 변수에 날짜가 31일까지 있는 달을 리스트 형태로 담아둡니다.

 

④ chosenMonth는 사용자가 앞, 뒤 버튼 등을 통해 월을 이동하여 선택한 달을 저장해 둘 변수입니다.

- 초기값을 0으로 두었지만, 앱이 처음 실행되면 현재 월이 저장됩니다. 

 

⑤ preMonth는 사용자가 선택한 달의 이전 달을 저장해 둘 변수입니다.

- 달력의 시작일(1일)이 일요일이 아니라면 보통 앞에 이전 달의 후반부 날짜를 회색으로 표시해둡니다.

- 이전 달이 몇 월인지 알아야 후반부 날짜를 어떻게 (ex. 28일 또는 30일 또는 31일) 표시할지 알 수 있습니다.

 

⑥ chosenMonthDays는 선택한 달의 날짜가 며칠까지 있는지 저장해 둘 변수입니다.

 

⑦ chosenMonthDates는 선택한 달의 달력에 실제 나타낼 날짜를 저장해 둘 변수로 리스트 형태로 담습니다.

- 선택한 달 뿐만 아니라, 선택한 달의 전반부(이전달의 후반부)와 선택한 달의 후반부(다음달의 전반부)를 모두 담게 될 것입니다.

- 이 변수에 저장된 값이 날짜를 나타내게 될 버튼 컴포넌트(btn1_1 ~ btn6_7)에 대입될 것입니다.

 

⑧ preMonthDays는 선택한 달의 이전달에 날짜가 며칠까지 있는지 저장해 둘 변수입니다.

 

⑨ chosenMonthStartingPoint 변수는 선택한 달의 1일이 무슨 요일에서 시작하는지 저장해 둘 변수로, 달력을 위한 가장 중요한 변수 중 하나입니다.

- 이 변수를 숫자로 저장하는 이유는 뒤에서 나오는데, 앱인벤터에서 요일을 숫자로 전달(반환)해주기 때문입니다.

- 1: Sunday, ...... 7: Saturday

 

⑩ year 변수는 연도를 저장해 둘 변수입니다.

 

⑪ btnDates는 블록의 접힌 부분을 더블 클릭해서 펼쳐보면 btn1_1 ~ btn6_7 까지 날짜를 표시할 버튼 42개를 리스트 형태로 담아둔 변수입니다.

 

 


 앱이 처음 시작되었을 때, 실행되는 블록입니다.

 

앱이 시작되었을 때 실행되는 블록

 

 

① 앱이 처음 시작되었을 때, do안의 블록을 실행합니다.

 

② 변수 chosenMonth를 현재 시간에서 달을 가져온 값으로 설정합니다.

- 이 때 월은 숫자로 저장됩니다.

 

③ 변수 year을 현재 시간에서 연도를 가져온 값으로 설정합니다.

 

④ setMonth 프로시저를 호출해 실행하는데, 이 때 setMonth에서 요구하는 매개변수 monthGap은 0으로 합니다.

- setMonth 프로시저의 monthGap은 사용자가 앞, 뒤 버튼을 눌렀을 때, 현재 선택되어진 달과의 차이를 표시할 매개변수입니다.

- 앱이 처음 시작되었을 때는 현재 시간의 월을 나타낼 것이므로 차이가 없어 monthGap을 0으로 해둡니다.

 

⑤ ~ ⑦: 각각에 해당되는 프로시저를 호출해 실행합니다.

 

 


다음은 setMonth 프로시저 블록입니다.

setMonth 프로시저는 달에 대한 정보(날짜의 수, 이전 달 정보 등등)를 처리하는 프로시저입니다.

 

setMonth 프로시저

 

 

① monthGap은 setMonth 프로시저에서 요구하는 매개변수입니다. 다른 곳에서 setMonth를 사용하고자 할 때는 monthGap에 대한 값을 입력해주어야 합니다. 

- 앱이 시작했을 때 블록을 살펴보시면 monthGap에 대한 값으로 0을 주었습니다.

- 이전 버튼(<)을 클릭(btn_back.click 블록 참조)했을 때, monthGap에는 -1을 줄 것이고, 

- 이후 버튼(>)을 클릭했을 때, monthGap은 1을 줄 것입니다.

 

② 만약 변수 chosenMonth에 monthGap을 더한 값이 13이라면,

- chosenMonth는 12, monthGap은 1이라는 의미로,

 

③ 12월 다음은 1월이므로 chosenMonth에 1을 입력합니다. 연도는 현재 연도에서 1을 더해줍니다.

 

④ 만약 변수 chosenMonth에 monthGap을 더한 값이 0이라면,

- chosenMonth는 1, monthGap은 -1이라는 의미로,

 

⑤ 1월 이전은 12월이므로 chosenMonth의 값을 12로 합니다. 연도는 현재 연도에서 1을 빼줍니다.

 

⑥ ②또는 ④의 경우가 아니라면 연도가 변할 일이 없으므로, 변수 chosenMonth에는 현재 값에 monthGap만큼(+1또는 -1) 더한 값을 저장합니다.

 

 ⑦ chosenMonthStartingPoint에 선택한 달의 시작일(1일)의 요일을 저장합니다.

Weekday 메소드

- Weekday 메소드는 인스턴트에 꽂힌 날짜의 요일을 숫자로 돌려줍니다. (1: Sunday,.....7: Saturday)

 

⑧ 만약 선택된 월에 1을 뺐을 때 0이라면

 

⑨ 선택된 달이 1월이라는 의미이므로 이전 달을 나타내는 변수인 preMonth에 12를 저장합니다.

 

⑩ ⑧이 아니라면 preMonth에는 선택된 달에 1을 뺀 값을 저장합니다.

 

⑪ ~ ⑬ 변수 preMonthDays의 값을 저장하는데,

⑪ 만약 변수 preMonth가 리스트 변수 months30에 포함되어 있다면 preMonthDays에는 30을 저장합니다.

 

⑫ 만약 변수 preMonth가 리스트 변수 months31에 포함되어 있다면 preMonthDays에는 31을 저장합니다.

 

⑬ ⑪ 또는 ⑫가 아니고 (2월을 의미합니다.), 연도를 4로 나누었을 때 나머지가 0이 아니라면, 

preMonthDays에는 28을 입력하고, 나머지가 0이라면 29를 입력합니다.

- 2월이 29일까지 있는 윤년은 4로 나누었을 때, 몫이 0이 되는 해입니다. (ex. 2004, 2008...)

 

⑭ 현재 선택된 달 날짜의 수를 저장해 둘 변수 chosenMonthDays의 값을 설정합니다. ⑪ ~ ⑬과 동일

 

 


다음은 setCalendar 프로시저 블록입니다.

setCalendar 프로시저의 경우 블록이 많아 세 부분으로 나누어 설명드리겠습니다.

 

# setCalendar 프로시저-1

 

setCalendar 프로시저-1

 

 

① 1부터 42까지 number를 1씩 증가하면서 do안의 블록을 실행합니다.

- 42까지 설정한 이유는 날짜 데이터를 표시하는 버튼 컴포넌트 42개가 리스트 변수 btnDates안에 있기 때문입니다.

 

② 만약 get number를 7로 나눈 나머지가 1이라면

- get number가 1, 8, 15, ...가 해당될 것입니다.

 

③ 리스트변수 btnDates에 있는 get number번째 항목의 글자색을 빨간색으로 지정합니다.

- btn1_1, btn2_1, btn3_1 등이 해당되므로 일요일이 되는 것입니다.

 

④ 만약 get number를 7로 나눈 나머지가 0이라면

- get number가 7, 14, 21, ...이 해당될 것입니다.

 

⑤ 리스트변수 btnDates에 있는 get number번째 항목의 글자색을 파란색으로 지정합니다.

- btn1_7, btn2_7, btn3_7 등이 해당되므로 토요일이 됩니다.

 

⑥ ②도 아니고 ④도 아니라면 리스트변수 btnDates에 있는 get number번째 항목의 글자색을 검정색으로 지정합니다.

- 평일을 나타내는 버튼 색을 지정하게 되는 것입니다.

 

 

# setCalendar 프로시저-2

 

setCalendar 프로시저-2

 

 

① 달력에 표시될 날짜를 저장하는 리스트 변수 chosenMonthDates를 빈 리스트로 설정합니다.

- setCalendar 프로시저는 앞, 뒤 버튼을 클릭할 때마다 호출이 되는데, 이전에 저장된 내용을 없애고 새로운 달의 데이터를 저장하기 위한 것입니다.

 

② 부터는 다음과 같은 달력을 가정하여 예와 함께 설명드리겠습니다.

달력 예시

 

② 만약 변수 chosenMonthStartingPoint가 1이 아니라면,

- chosenMonthStartingPoint는 setMonth 프로시저에서 결정되며 1에서부터 7까지의 숫자 중 하나가 저장됩니다.

- 1은 일요일, 2는 월요일... 7은 토요일을 의미합니다.

- 달이 시작하는 요일이 일요일이 아니라면 그 앞에는 이전 달의 날짜를 표시해야 할 것입니다.

 

③ 변수 chosenMonthStartingPoint에 저장된 값에 1을 뺀 값에서부터 1까지 number를 -1씩 증가하며 do 안의 블록을 실행합니다.

- 위의 달력에서 시작일의 요일이 월요일이므로 chosenMonthStartingPoint에는 2가 저장되어 있으므로, 1번만 실행이 됩니다.

 

④ 리스트 변수 chosenMonthdDates에 변수 preMonthDays(이전달의 날짜 수) - get number + 1한 값을 저장합니다.

- 위의 달력에서 이전달(7월)의 날짜 수는 31이고, get number는 1, 그래서 31-1+1 = 31이 더해집니다.

- 결과적으로 chosenMonthDates는 현재 [31]로 저장되어 있습니다.

 

===== 추가적 설명 =====

- 만약 chosenMonthStartingPoint가 3(화요일)이라 가정하고, 이전달이 7월이라고 가정한다면 get number가 2에서 1까지 2번 반복하게 됩니다.

-- get number가 2일때, (31-2+1=30) chosenMonthDates: [30]

-- get number가 1일때, (31-1+1=31) chosenMonthDates: [30, 31]

====================

 

⑤ 날짜를 표시할 버튼 컴포넌트를 모아둔 btnDates의 get number번째 항목의 글자색을 회색으로 설정합니다.

- 위의 예에서는 number가 1, 한번만 실행되므로, btnDates의 첫 번째 항목 btn1_1의 글자색만 회색으로 설정합니다.

- 이전 달의 날짜 색상을 회색으로 표시하는 내용입니다.

 

⑥ 1부터 현재 선택한 월의 날짜 수(chosenMonthDays)까지 1씩 증가하며 do 안의 블록을 실행합니다.

- 위 달력에서는 현재 8월이 선택한 월이므로 chosenMonthDays에는 31이 저장되어 있습니다.

 

⑦ 리스트 변수 chosenMonthDates에 get number를 추가합니다.

- ⑦ 이전에 chosenMonthDates에는 [31]이 저장되어 있었으므로,

- get number가 1일때 chosenMonthDates: [31, 1]

- get number가 2일때 chosenMonthDates: [31, 1, 2]

- get number가 31일때 chosenMonthDates: [31, 1, 2, .........., 31]이 저장됩니다.

- chosenMonthDates의 항목 개수는 총 32개가 저장되게 됩니다.

 

⑧ 1부터 42에서 chosenMonthDates의 항목 개수를 뺀 값까지 number를 1씩 증가하며 do 안의 블록을 실행합니다.

- 위 ⑦에서 chosenMonthDates의 항목 개수가 32개 였으므로, 1부터 10까지 do 안의 블록을 실행합니다.

 

⑨ chosenMonthDates에 get number를 추가합니다.

- chosenMonthDates에는 이미 [31, 1, 2, ........., 31]이 저장되어 있었으므로,

- get number가 1일때 chosenMonthDates: [31, 1, 2, ........., 31, 1]

- get number가 2일때 chosenMonthDates: [31, 1, 2, ........., 31, 1, 2]

- get number가 10일때 chosenMonthDates: [31, 1, 2, ........., 31, 1, 2, ....., 10]

- 선택된 달 마지막 날짜 뒤에 다음 달의 앞의 날짜를 채워넣는 것입니다.

 

 

# setCalendar 프로시저-3

 

setCalendar 프로시저-3

 

 

① 1부터 42까지 number가 1씩 증가하며 do 안의 블록을 실행합니다.

 

② 리스트 변수 btnDates에 get number번째 항목의 글자를 리스트 변수 chosenMonthDates에 get number번째 저장된 값으로 설정합니다.

- 실제 달력에 날짜를 보이게 하는 것입니다.

- 위 달력의 예에서 보자면 btn1_1: 31, btn1_2: 1, btn1_3: 2 .....와 같이 표시됩니다.

 

③ 변수 chosenMonthDays에 변수 chosenMonthStartingPoint를 더한 값에서 부터 42까지 number가 1씩 증가하며 do 안의 블록을 실행합니다.

- 위 달력의 예에서 보자면 chosenMonthDays=31, chosenMonthStartingPoint=2이므로 33부터 시작합니다.

- 선택된 월의 마지막 날짜 다음에 올 다음 달의 초반 날짜가 해당됩니다.

 

④ 리스트 변수 btnDates의 get number번째 항목의 글자색을 회색으로 설정합니다.

 

⑤ 만약 btn4_1의 글자(숫자)가 btn5_1의 글자(숫자)보다 크다면,

- 위 달력에서 btn4_1의 글자(숫자)는 21, btn5_1의 글자(숫자)는 28입니다.

 

⑥ 5번째 주의 버튼들을 담고 있는 hor_5thWeek와 6번째 주의 버튼들을 담고 있는 hor_6thWeek를 보이지 않게 합니다.

 

⑦ 만약 btn5_1의 글자(숫자)가 btn6_1의 글자(숫자)보다 크다면,

- 위 달력에서 btn5_1의 글자(숫자)는 28, btn6_1의 글자(숫자)는 4입니다.

- 이 경우 6째주는 사용자에게 보여 줄 필요가 없습니다.

 

⑧ 6번째 주의 버튼들을 담고 있는 hor_6thWeek를 보이지 않게 합니다.

 

⑨ 달력 상단의 글자를 월 연도 (ex. August 2002)로 표시합니다.

 

 


 

다음은 setComponentStyle 프로시저 블록입니다.

 

setComponentStyle 프로시저

 

① 확장 프로그램 MakeViewUp의 배경색을 흰 색으로 설정합니다.

- MakeViewUp 확장 프로그램의 명령을 받게되는 컴포넌트들은 MakeViewUp에서 설정한 배경색을 자동으로 가지게 됩니다.

- 여기서는 앞, 뒤 버튼이 MakeViewUp의 명령을 받고 이 버튼들의 배경색을 흰 색으로 지정하기 위한 것입니다.

 

② btn_back 컴포넌트의 테두리 너비(borderWidth)를 1로 설정하고, 색은 회색으로 하여 테두리를 그립니다.

 

③ btn_back 컴포넌트의 모서리를 둥글게 하는데, 둥글기 정도는 8로 설정합니다.

 

④ btn_back 컴포넌트를 살짝 떠 있는 느낌이 들도록 보이게 만드는데, 떠 있는 정도는 2로 설정합니다.

 

⑤ ~ ⑦: ② ~ ④와 동일

 

 


 

다음은 setDatesBackgroundcolor 프로시저 블록입니다.

이 블록은 오늘 날짜에  배경색을 입혀 눈에 띄게 하려는 용도입니다.

 

setDatesBackgroundcolor 프로시저

 

 

① 리스트 변수btnDates에 들어있는 각각의 항목(item)마다 do 안의 블록을 적용시킵니다.

 

② 각각의 항목(item)의 배경색을 흰색으로 설정합니다.

- setDatesBackgroundcolor 프로시저는 앞, 뒤 버튼을 누를 때마다 호출이 되는데, 이전에 오늘 날짜로 지정되어 배경색이 다른 색으로 지정되어 있을 경우 이를 다시 흰색으로 원상복귀 시키기 위한 것입니다.

 

③ ~ ④ 만약 변수 chosenMonth에 저장된 값과 오늘 날짜의 월과 같고, 변수 year에 저장된 값과 오늘 날짜의 연도가 같다면,

 

⑤ 확장 프로그램 MakeViewUp의 배경색을 스카이블루와 같은 색으로 설정해 둡니다.

 

⑥ 오늘 날짜를 나타내는 버튼의 모서리를 둥글게 하는데 그 크기는 32로 설정합니다.

- 우측에 접힌 부분을 더블 클릭하여 펼쳐보면 다음과 같은 블럭이 나옵니다.

접혀져 있는 부분

- 리스트 변수 btnDates에서

- DayofMonth는 인스턴트(instant)에 입력된 시간에 해당하는 날짜를 가져옵니다. 오늘이 2일이면 2를 가져옵니다.

- 아까 위에서 예를 든 달력에 chosenMonthDates에는 [31, 1, 2, 3, 4, ...., 10]이 들어가 있었고, 여기에서 2일은 3번째 항목입니다. 따라서 btn1_3의 모서리를 둥글게 해주어야 합니다.

- 오늘이 2일이라 가정하면, index에는 Clock1.DayofMonth 2 + chosenMonthStartingPoint 2 - 1 = 3이 됩니다.

- 위에서 언급했듯이 MakeViewUp의 명령을 받는 컴포넌트는 MakeViewUp에서 설정한 배경색을 가지게 되므로, 따로 설정을 해주지 않더라도 오늘 날짜에 해당되는 버튼의 배경색은 스카이블루가 됩니다.

 

 


 

이상으로 앱인벤터에서 달력 만들기 강좌였습니다.

이 방법 이외에도 달력을 만들 수 있는 알고리즘은 다양하게 생각할 수 있을 것입니다.

 

본 포스팅을 참고하셔서, 자신만의 알고리즘을 만들어보세요~~

 

댓글