공부/자바(JAVA)

자바(Swing) - 달력 구현하기

도도-도윤 2017. 10. 28. 15:12

자바(Swing) - 달력 구현하기


자바(Swing)으로 달력 구현하는 방법에 대해서 소개하겠습니다.



1. 자바 달력 만들기


작성 소요 기간: 2017-10-27 ~ 2017-10-28일

1일: 3시간~7시간

2일: 5시간


 

 자바 - 스윙(Swing) 달력 완성




2. UI 디자인




 설명) 보라색, 초록색으로 표기한 것은 고정된 영역이고, 파란색과 빨강색은 반복되는 영역입니다.





 크게 달력을 구성하는 화면 구성은 이런 형태입니다.


171028 - UI.pptx



3. 전략("자료구조"적인 생각)


Calendar 패키지를 사용하였습니다.

전략으로는 먼저 "일자" 대비로 요일을 환산하는 것을 하나 작성하고, 종료 일자를 작성하는 것입니다.

앞서 파란색으로 표기한 하나가 반복된다고 표현하였습니다.


 

  171028 - UI - 2.pptx


 파란색 영역이 반복되는데, 이 영역을 프로그래밍 코드로 표현하면

 

 1차원 배열처럼 표현할 수 있겠습니다.

 

생각을 해볼만한 주제가 되는데, [][] (2차원)배열로 표현할 것이냐, 1차원 배열로 표현할 것이냐는 코드 작성자의 역량입니다.

초기에 접근한 전략으로는 2차원 배열로 접근하였습니다.


  for ( int i = 0; i < 5; i ++ )

 {

       for ( int j = 0; j < 7; j++ );

 }

 반복문의 접근횟수를 살펴보면, O() 이 됩니다.

 O()번 접근하는게, 성능에서 효율적입니다.

 물론, 달력(Calendar) 프로젝트에서는 그렇게 많은 성능을 요구하는 작업을 하진 않습니다.


아래의 코드를 살펴보면,




 "가로(Weight) x 세로(Height)"의 사이즈로 반복문 한번으로 처리해도 됩니다.


"공간" 문제로 생각해보면,

jBtn[] 배열의 공간을 예를 들면, "5 x 7 = 35개로 처리할 것이냐?", "2월의 경우 - 28일" 또는 "2017년 10월의 마지막 31일"을 기준으로 처리할 것이냐 등을 고민해볼 수 있습니다.

저는 메모리 공간을 35개를 사용해서 화면 출력을 위주로 작성 하였습니다.



4. Calendar 클래스에 대해서


public void calendarSet(int year, int month ) {

       calendar.set(year, (month - 1), 1);
  
       //calendar.set(Calendar.YEAR, Integer.valueOf ( year ) ); 
       //calendar.set(Calendar.MONTH, Integer.valueOf ( month - 1 ) ); // 시작 일이 1이 아닌 0부터 시작함. (-1)
       //calendar.set(Calendar.DAY_OF_MONTH, 1 );        // 처음 시작요일 설정
    
       //System.out.println( calendar.get(Calendar.YEAR) + "-" + calendar.get(Calendar.MONTH) + "야" + month );
  
       // 시작 일자(주 요일 기준 - 환산)
       startDayOfWeek = calendar.get( Calendar.DAY_OF_WEEK ) ;
  
       // 마지막 일자
       lastDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);

       // 기본값 설정
       cmbYear.setSelectedItem( calendar.get( Calendar.YEAR ) );
       cmbMonth.setSelectedItem( calendar.get( Calendar.MONTH) + 1 );
 }

 Calendar 클래스를 사용하면, 바보스럽다고 보여지는 부분이기도 합니다.

 사람이 이해할 수 있는 월은 (1~12)인데, 반면 Calendar 클래스가 이해하는 월은 (0~11)입니다.

 물론 갯수로는 일치합니다. 12개.


0

1

2

3

4

5

6

7

8

9

10

11

 사람

1

2

3

4

5

6

7

8

9

10

11

12

 Calendar 클래스

0

1

2

3

4

5

6

7

8

9

10

11


 크게 Calendar 클래스를 사용하는데는 어렵진 않습니다.

 
 startDayOfWeek = calendar.get( Calendar.DAY_OF_WEEK ) 은 설정된 Calendar의 요일을 반환해주는 역할을 합니다.
 예를 들면,



1

2

3

4

5

6

7

 요일









 1~7 범위 내에서 반환을 해줍니다.




5. 알고리즘에 대해서

Calendar 클래스를 사용하면, 예를 들면, 윤년 계산식 등을 구현할 필요가 없습니다.

패키지 내에 알고리즘이 작성되어 있기 때문입니다.


모르면 그렇고, 알면 도움이 되는 달력 알고리즘을 소개하고자 합니다.


int CheckYUN(int parm_year)
{
     if((parm_year % 4 == 0 && parm_year % 100 != 0) || parm_year % 400 == 0)
     return 29;
     else return 28;
}

 윤년 알고리즘의 예



 int totalday[]={0,31,28,31,30,31,30,31,31,30,31,30,31}; // 각 달의 총일 수 (첫번째 수는 제외)


 lastyear = year - 1; // 작년 말까지 진행된 요일을 계산하기 위해 lastyear를 선언

 day = (lastyear + ( lastyear / 4 ) - ( lastyear / 100 ) + ( lastyear / 400 ) + 1 ) % 7 ;


 /* lastyear : 1년은 365일, 1년이 지날때마다 요일이 한번 늘어난다 (365를 7로 나누면 1이기 때문) +

  ( lastyear / 4 ) - ( lastyear / 100 ) + ( lastyear / 400 ) : 윤년의 다음연도는 요일이 두번 늘어므로, lastyear까지 있던

  모든 윤년을 더한다

  +1 : 1년 1월 1일은 일요일이 아니라 월요일이므로 1을 더한다 (0년은 없다)

  %7 : 요일은 7가지 이므로, 앞의 계산값을 7로 나눈 나머지가 year년 1월의 첫요일 뜻한다.

  나머지값에 따라서

 

  0: 일요일, 1:월요일, 2:화요일, 3:수요일, 4:목요일, 5:금요일, 6:토요일 */


 for(i=1;i<month;i++) // year년 month월 직전까지의 총 일 수를 구하기 위한 for문
        day+=totalday[i];
 
 day%=7; // year년 month월 1일의 시작 요일

 시작 요일을 구하는 알고리즘 - 예



6. 완성 시연 영상



 


 시연 - 달력 영상




7. 소스코드


/*
 * 2017-10-28
 * 프로젝트명: 달력 구현
 * 작성자: dodo(rabbit.white@hanmail.net)
 */


 package JCalendar;

 import java.awt.BorderLayout;
 import java.awt.EventQueue;

 import javax.swing.JFrame;
 import javax.swing.JPanel;
 import javax.swing.border.EmptyBorder;
 import java.awt.Color;
 import java.awt.Dimension;

 import javax.swing.JLabel;
 import javax.swing.JButton;
 import javax.swing.JComboBox;
 import javax.swing.JScrollPane;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.Calendar;


 public class FrameMain extends JFrame implements ActionListener {

          private JPanel contentPane;
          private JPanel panCalendar;
          private JScrollPane scrollDatePane;
 
          private JComboBox<Integer> cmbYear;
          private JComboBox<Integer> cmbMonth;
 
          private JButton btnPrev;
          private JButton btnNext;
          private JButton btnQuery;

          private JPanel[] itemPane;
          private JButton[] jBtn;
 
          private Dimension dim;
 
          private Calendar calendar;
 
          private JPanel panHeader;
          private JPanel panBody;
 
          private int startDayOfWeek;
          private int lastDay;
 
          /**
           * Launch the application.
           */
          public static void main(String[] args) {
                   EventQueue.invokeLater(new Runnable() {
                       public void run() {
                             try {
                                 FrameMain frame = new FrameMain();
                                 frame.setVisible(true);
                             } catch (Exception e) {
                                 e.printStackTrace();
                             }
                      }
               });
          }


          /**
           * Create the frame.
           */
          public FrameMain() {
                   setTitle("달력");

                   calendar = Calendar.getInstance();
  
                   setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                   setBounds(100, 100, 1000, 600);
                   contentPane = new JPanel();
                   contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
                   setContentPane(contentPane);
                   contentPane.setLayout(null);
  
                    initComponent();
                    createHeader ( calendar );
                    calendarSet( calendar.get ( Calendar.YEAR ), ( calendar.get( Calendar.MONTH ) + 1 ) );
                    update ( calendar );
          }
 
          public void initComponent() {
  
                    panCalendar = new JPanel();
                    panCalendar.setBounds(12, 40, 960, 550);
                    contentPane.add(panCalendar);
                    panCalendar.setLayout(null);
  
                    panHeader = new JPanel();
                    panHeader.setBounds(0, 0, 948, 41);
                    panCalendar.add(panHeader);
                    panHeader.setLayout(null);
  
                    cmbYear = new JComboBox<Integer>();
                    cmbYear.setBounds(121, 11, 60, 20);
                    panHeader.add(cmbYear);
  
                    cmbMonth = new JComboBox<Integer>();
                    cmbMonth.setBounds(193, 11, 60, 20);
                    panHeader.add(cmbMonth);
  
                    btnPrev = new JButton("이전");
                    btnPrev.addActionListener(this);
                    btnPrev.setBounds(12, 10, 97, 20);
                    panHeader.add(btnPrev);


                    btnQuery = new JButton("조회");
                    btnQuery.addActionListener(this);
                    btnQuery.setBounds(265, 10, 97, 20);
                    panHeader.add(btnQuery);
  
                    btnNext = new JButton("다음");
                    btnNext.addActionListener(this);
                    btnNext.setBounds(374, 10, 97, 20);
                    panHeader.add(btnNext);
  
  
                    JScrollPane scrollDatePane = new JScrollPane();
                    scrollDatePane.setBounds(0, 58, 948, 400);
                    panCalendar.add(scrollDatePane);
  
                    panBody = new JPanel();
                    scrollDatePane.setViewportView(panBody);

                    dim = new Dimension(5, 7);
  
                    jBtn = new JButton [ (int)dim.getWidth() * (int)dim.getHeight() ];
                    itemPane = new JPanel[ (int)dim.getWidth() * (int)dim.getHeight() ];
                    panBody.setLayout(new GridLayout( (int)dim.getWidth() , (int)dim.getHeight() ) );
          }
 
          public void calendarSet(int year, int month ) {

                    calendar.set(year, (month - 1), 1);
  
                    //calendar.set(Calendar.YEAR, Integer.valueOf ( year ) ); 
                    //calendar.set(Calendar.MONTH, Integer.valueOf ( month - 1 ) ); // 시작 일이 1이 아닌 0부터 시작함. (-1)
                    //calendar.set(Calendar.DAY_OF_MONTH, 1 );        // 처음 시작요일 설정
    
                    //System.out.println( calendar.get(Calendar.YEAR) + "-" + calendar.get(Calendar.MONTH) + "야" + month );

                    // 시작 일자(주 요일 기준 - 환산)
                    startDayOfWeek = calendar.get( Calendar.DAY_OF_WEEK ) ;
  
                    // 마지막 일자
                    lastDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);

                    // 기본값 설정
                    cmbYear.setSelectedItem( calendar.get( Calendar.YEAR ) );
                    cmbMonth.setSelectedItem( calendar.get( Calendar.MONTH) + 1 );
          }
          
          public void update( Calendar calendar ) {

                    int width = (int)dim.getWidth();
                    int height = (int)dim.getHeight();
                    int size = width * height;
  
                    int day = 0;
                    int count = 1;
                    boolean state = false;
  
                    // 달력 생성
                    for (int i = 0; i < size ; i++) {
   
                           if ( count >= startDayOfWeek && count < ( lastDay + startDayOfWeek ) ) {
                               state = true;
                               day++;
                           } // end of if
   
                           // itemPane 존재 여부
                           if ( itemPane [ count - 1 ] == null ) {

                               itemPane [ count - 1 ] = new JPanel();
                               itemPane [ count - 1 ].setLayout( null );
                               itemPane [ count - 1 ].setSize( 100, 100 );
    
                            if ( !state )
                           {
                                jBtn [ count - 1 ] = new JButton( "" );
                           }
                           else
                           {
                                jBtn [ count - 1 ] = new JButton( day + "" );
                                //panBody.add( jBtn[i][j] );

                               state = false;
              
                            } // end of if


                            jBtn [ count - 1 ].setSize( 50, 20 );

                           itemPane [ count - 1 ].add( jBtn [ count - 1 ] );
                           panBody.add( itemPane [ count - 1 ] );
    
                     } // end of if
   
                     // itemPane 업데이트
                     else
                     {
                               // System.out.println("참" + day );
                               if ( !state )
                               {
                                         jBtn [ count - 1 ].setText( "" );
                               }
                               else
                               {
                                         jBtn [ count - 1 ].setText( day + "" );
                                         //panBody.add( jBtn[i][j] );

                                         state = false;
                               } // end of if
    
                     } // end of if

                     count++;
   
               } // end of for
  
          }


          public void createHeader(Calendar calendar) {

                    // 년도 생성
                    for ( int i = calendar.get( Calendar.YEAR ) + 30 ; i >= 1900; i-- )
                    {
                              this.cmbYear.addItem( i );
                    } // end of for
  
                    // 월 생성
                    for ( int i = 1; i <= 12; i++ ) {
                              this.cmbMonth.addItem( i );
                    } // end of for

          }
 
          @Override
          public void actionPerformed(ActionEvent arg) {
  
                    if ( arg.getSource().equals( this.btnPrev ) ) {  
                          int year = Integer.valueOf( this.cmbYear.getSelectedItem().toString() );
                          int month = Integer.valueOf( this.cmbMonth.getSelectedItem().toString() );

                          //System.out.println(year + "년" + month );

                          month--;
                          //System.out.println(year + "년" + month );
   
                          calendarSet( year, month );
                          update(calendar);
   
                    } // end of if
  
                    if ( arg.getSource().equals( this.btnQuery ) ) {

                           System.out.println("조회이에요");
                           int year = Integer.valueOf( this.cmbYear.getSelectedItem().toString() );
                           int month = Integer.valueOf( this.cmbMonth.getSelectedItem().toString() );
   
                           calendarSet( year, month );
                           update(calendar);
   
                    } // end of if

                    if ( arg.getSource().equals( this.btnNext ) ) {
                           System.out.println("다음이에요");
   
                           int year = Integer.valueOf( this.cmbYear.getSelectedItem().toString() );
                           int month = Integer.valueOf( this.cmbMonth.getSelectedItem().toString() );

                           //System.out.println(year + "년" + month );

                           month++;
                           //System.out.println(year + "년" + month );
   
                           calendarSet( year, month );
                           update(calendar);
   
                    } // end of if
  
          }
 
}

 FrameMain.java


[소스코드]

171028 - JavaSwingDate.zip



8. 참고자료

1. C-언어-달력-윤년-계산-하기, http://1woo.tistory.com/entry/C-언어-달력-윤년-계산-하기 [언제쯤 빛이..], "본문 5. 알고리즘"

2. [C언어] 달력 출력 프로그램], http://www.rebas.kr/15, "본문 5. 알고리즘"

3. [JAVA] Calendar 클래스 (달력 출력), http://hyeonstorage.tistory.com/205, "참고"

171028 - UI - 2.pptx
0.04MB
171028 - UI.pptx
0.04MB
171028 - JavaSwingDate.zip
0.0MB