말로 설명하는 스윙 프로그램
컨테이너 계열
컨테이너란 다른 컴포넌트를 포함하는 영역을 나타내는 요소들이다.
JPanel
JPanel은 스윙 GUI에서 가장 기본이 되는 컴포넌트로 윈도우 상의 사각형 영역을 나타낸다. 위치, 크기, 배경색 등의 정보를 가지고 있으며, 다른 컴포넌트를 추가하여 배치하기 위한 기본 틀이라고 볼 수 있다. 스윙에서는 모든 요소들의 배치는 사각형 틀을 이용해야 하므로 필요할 때마다 다른 컴포넌트들을 담을 패널을 생성하여 사용하게 된다.
JFrame
프레임은 화면 상에 독립적으로 존재하는 윈도우 부분을 나타낸다. 보통 스윙 프로그램에는 하나의 메인 프레임이 있고 그 안에서 마우스나 키보드를 처리하는 형태로 프로그램이 구성된다. 그리고 메인 클래스의 역할도 하게 된다.
Container
컨테이너는 다른 컴포넌트를 담을 수 있는 클래스로 JPanel이나 JFrame도 컨테이너다.
- J가 붙은 이름은 스윙 라이브러리 클래스에서 추가된 것들이고 J가 붙지 않은 것은 awt 라이브러리에서부터 있었던 기본 클래스들이다.
- JFFrame이나 JDialog는 contentPane이라는 패널을 가진다. 이것은 그 윈도우에서 컴포넌트를 추가할 수 있는 영역(타이틀, 테두리, 스크롤 등을 제외한 영역)을 가진 패널로 우리는 getContentPane()을 통해 이 패널을 돌려받을 수 있다. 단 이것은 JPanel이 아니어서 Container 타입으로 받아야 한다.
JDialog
대화상자라고도 하는데, 팝업으로 떠서 간단한 정보 보여주기나 입력받기를 위해 사용하는 창이다. 보통으로 확인, 취소, 닫기 등의 버튼을 가지고 있어서 보여졌다가 닫으면 사라진다. (대화상자는 없애지 않고 그냥 둔 채로 감추기를 하기도 한다. 감추기란 setVisible(false)를 하면 화면에서 사라진다.)
* 화면에서 사라지면 마우스나 키보드 입력을 받을 수 없다. 마우스나 키보드 입력은 포커스를 가진 컴포넌트만 받을 수 있다. 포커스는 화면의 영역을 차지하고 있는 컴포넌트가 가지게 되는데 보통 클릭을 하면 그 컴포넌트가 포커스를 가져가게 된다.
기타 JComponent 류
JLabel
라벨은 화면 상의 네모 영역을 가지고 그 안에 텍스트를 보여줄 수 있는 컴포넌트다. (그 안에 뭘 넣을 수는 없음. 즉 자식 컴포넌트를 갖지는 못한다.) 텍스트 설정이나 배경색 변경, 텍스트 색깔과 폰트 변경, 텍스트 정렬 방식 설정 등이 가능하다.
화면 상에 텍스트를 나타내기 위해서는 JLabel 컴포넌트를 이용해야 한다.
JButton
버튼은 우리가 일반적으로 알고 있는 버튼과 같다. 네모난 영역을 차지하고 클릭이나 마우스 다운에 대해 모양이 바뀌기도 하며, 버튼 이름을 텍스트로 가진다. (이미지 버튼도 가능함) 버튼을 누르게 되면 어떤 동작을 해야 하는데 이것을 담당할 핸들러를 사용하는 프로그램에서 지정해 주어야 한다. 버튼은 아무 일도 하지 않는 기본 핸들러를 가지고 있다.
JTextField
텍스트 필드는 한줄 텍스트 입력을 위한 컴포넌트다. 간단한 입력을 키보드로 타이핑할 수 있게 해주는데, 프로그램에서는 여기에 있는 텍스트를 가져오는 getText와 보여질 텍스트를 설정하는 setText를 할 수 있다.
JScrollPane
스크롤페인은 패널인데 상하 또는 좌우 스크롤을 만들어주는 패널이다. 여기에 포함된 컴포넌트의 크기가 이 패널보다 크면 자동으로 스크롤이 생성된다. 일반적으로 스크롤페인에는 JPanel을 하나 포함하게 되는데, JPanel의 크기에 따라 스크롤이 생성된다.
레이아웃
레이아웃이란 네모난 영역을 구성하는 몇가지 옵션을 나타낸다. 컨테이너에 컴포넌트를 배치하기 위해서 차례로 줄세울지(FlowLayout), 상하좌우와 센터가 있는 이클립스 같은 모양으로 할지(BorderLayout), 그리고 바둑판 모양으로 할지(GridLayout) 등을 정하게 된다. 다른 모든 GUI 프로그래밍 방식과 마찬가지로 스윙에서도 화면을 구성하는 것이 가장 복잡하고 번거로운 작업이다. 그러나 익숙해지면 대부분 비슷비슷하게 사용되므로 적당히 코드를 가져다 약간만 수정하면 별 문제없이 원하는 모양을 만들 수 있다.
- 또하나 대표적인 레이아웃이 카드 레이아웃이다. 이클립스에서 여러 개 소스 파일이 열리는데 그 중 하나를 편집창에 보여주는 것이 카드레이아웃인데, 이 때는 사실 여러 개의 JPanel이 겹쳐져 있고 위쪽의 탭을 선택하면 그에 해당하는 패널이 제일 위로 올라와서 보여지게 된다.
컨테이너에 setLayout을 통해 어떤 레이아웃을 사용할 것인지 지정하게 된다. 그리고 나면 add 메소드를 통해 원하는 컴포넌트를 원하는 위치에 넣게 된다.
스윙에서는 컴포넌트들을 배치하고 나면 각 컨포넌트의 크기와 레이아웃을 고려하여 전체의 크기를 정하게 되는데 이것을 컨테이너가 가진 pack() 메소드가 한다. 그래서 보통 요소 컴포넌트들을 만들어서 추가하고 pack을 통해 전체적인 위치가 잡힌다고 볼 수 있다. (그러므로 크기나 위치는 그 전에는 올바른 값을 갖고 있지 않음)
이벤트 핸들러
이벤트는 사용자가 GUI 프로그램과 상호작용하기 위해 입력한 액션(마우스, 키보드, 터치 등)을 OS가 프로그램에 전달하는 방식이다. 그러므로 스윙 프로그램은 항상 대기상태로 있다가 뭔가 입력 이벤트가 들어오면 그에 대해 대응하는 동작을 수행하게 된다. 이러한 이벤트는 해당 영역을 차지하고 있고 포커스를 가진 컴포넌트에게 전달된다. 스윙에서는 컴포넌트들이 어떤 이벤트를 받게 되면 이것에 대응해서 어떤 동작을 할지를 정하는 이벤트 핸들러를 프로그램에서 등록하게 된다. 예를 들어 카톡에서 (전송) 버튼을 누르면 메시지가 보내지게 된다. 프로그램은 전송 버튼이 눌려졌다는 이벤트가 발생할 때 어떤 일을 할지 코드로 작성해 주어야 하고 이것을 이벤트 핸들러 함수라고 한다.
컴포넌트에 따라 받을 수 있는 이벤트가 정해진다. 예를 들어 JButton은 버튼이 클릭되었음을 알려주는 ActionEvent라는 것을 받게 된다. 많은 컴포넌트들이 마우스가 클릭되었음을 알려주는 MouseEvent를 받게 된다. 또한 키보드가 타이핑되었음을 알려주는 KeyEvent가 있다. 또한 마우스가 움직이거나 드래그 되고 있음을 알려주는 MouseEvent도 있다.
프로그램에 이벤트가 전달되면 스윙의 가장 핵심부에 있는 이벤트디스펜서 무한루프에 의해 해당 컴포넌트에 대해 등록해둔 이벤트 핸들러를 호출하게 된다. 카톡에서 (전송) 버튼이 눌려지면 그 버튼의 이벤트 핸들러로 등록되어 있는 메소드가 불려질 것이다. 이것은 인터페이스구현 방식에 의해 전달된 객체를 통해 등록되고 불려진다. (DIP 원칙)
스윙 프로그램에서 클래스 구성
이러한 컴포넌트와 컨테이너, 이벤트 핸들러로 프로그램을 실제 구성하는 방식을 살펴보자. GUI 프로그램은 보통 JFrame을 상속하는 클래스가 메인 클래스가 되고 여기서 화면을 구성하는 코드 부분을 호출하게 된다. 버튼이나 텍스트필드, 라벨 등의 컴포넌트로 화면을 구성하고 배치를 하는 부분이 수행되는데, 여기서 컴포넌트는 생성, 설정, 추가 과정을 거치게 된다.
이 때 필요하다면 그 컴포넌트에 이벤트 핸들러를 등록한다. 이벤트 핸들러는 필요한 경우에만 등록하는데, 그것을 위해서는 이벤트 핸들러 인터페이스를 구현한 객체를 전달하여 등록한다(addOOOListener). 예를 들어 마우스 이벤트 핸들러가 등록되면 마우스 이벤트가 그 핸들러의 해당 메소드를 호출하면서 전달된다. 컴포넌트가 제공하는 기본적인 기능(getText, setText 같은 것들)만 이용한다면 핸들러를 등록할 필요가 없다.
이 때 결정해야 하는 것 중에 하나가 컴포넌트를 생성해서 가지는 변수 중에서 어떤 것을 필드로 선언하고 어떤 것은 지역변수로 선언할 것인가 이다. 기본 원칙은 가능한 한 변수는 지역변수로 선언하는 것이 좋다는 것이다. 그러나 두 개 이상의 함수에서 필요로 하는 경우는 필드로 선언해야 한다. 이것은 화면을 구성하는 메소드에서 만들어진 컴포넌트가 이벤트 핸들러에서 사용되어야 하는 경우가 대표적이다. 그러나 많은 경우 이벤트 핸들러 함수가 전달해 주는 이벤트가 필요한 정보를 대부분 가지고 있어서 직접 컴포넌트 객체를 참조하는 변수를 사용하지 않아도 되는 경우가 많다.
다음으로 필요에 따라 어떤 부분을 다른 클래스로 독립하기도 한다. 화면을 구성하는 부분이 너무 복잡하고 그에 따른 이벤트 핸들러도 너무 많다면 이것을 모두 하나의 클래스에 가지고 있는 것은 어려운 일이다. 이런 경우 화면을 구성하는 부분을 그게 나누어 어느 것을 다른 클래스로 보내기도 한다. 예를 들어 테트리스 프로그램이라면 블록이 쌓이는 부분을 별도의 클래스로 빼서 키보드 이벤트에 따라 블록이 내려가는 것을 담당할 것이다. 그리고 우측의 패널에는 단계, 점수, 다음 블록 등을 보여주는 일을 담당하는 클래스가 있을 것이다. 이런 클래스는 보통 JPanel을 상속해서 그 패널에 컴포넌트를 추가하거나 구성하는 일과 그에 따른 이벤트 핸들러를 가지는 클래스가 된다. 이런 클래스 객체들을 전체 JFrame의 컨텐츠 페인이 포함하고 있을 것이다.