자바 UI - 01) Swing (Swing 소개 ,이벤트 디스패칭 스레드 ,Swing 컨테이너)
Swing 소개
UI(User Interface) 프로그램은 윈도우, 메뉴, 버튼, 라디오, 리스트 등 시각적인 컴포넌트를 제공해서 사 용자와 상호작용하도록 돕는다.
자바는 이러한 UI 프로그램을 개발할 수 있도록 JDK에서 JFC(Java Foundation Classes)를 제공한다.
JFC는 UI 프로그램을 만들기 위한 클래스들의 모음으로, AWT(Abstract Window Toolkit)와 Swing(스윙) 을 제공하고 있다. AWT는 java.awt 패키지로, Swing은 javax.swing 패키지로 사용 가능하다.
AWT는 여러 운영체제들이 공통적으로 가지고 있는 컴포넌트만 사용하므로 컴포넌트 수가 제한적이지만,
Swing은 자바에서 직접 제공하는 컴포넌트이기 때문에 종 류가 매우 다양하다.
Swing의 단점은 자바가 직접 컴포넌트를 생성하기 때문에 AWT에 비해 CPU 와 메모리를 상대적으로 많이 사용한다는 것이다.
AWT로 작성한 간단한 윈도우 프로그램
import되는 내용을 보면 모두 java.awt 패키지를 사용
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class App extends Frame{
public App() {
// 제목 설정
setTitle("AWT App");
//윈도우 크기 설정
setSize(300, 100);
//Button 추가
add(new Button("Ok"), BorderLayout.SOUTH);
// 종료 버튼 처리
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String[] args) {
App app = new App();
app.setVisible(true);
}
}
- 실행 결과 -
Swing으로 작성한 간단한 윈도우 프로그램
Swing은 AWT가 제공하는 것을 공유하기 때문에 import되는 내용을 보면 java.awt 패키지와 javax.swing 패키지를 사용
import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
public class App2 extends JFrame{
public App2() {
setTitle("Swing App");
setSize(300, 100);
getContentPane().add(new JButton("OK"), BorderLayout.SOUTH);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String[] args) {
App2 app = new App2();
app.setVisible(true);
}
}
- 실행 결과 -
윈도우를 만들기 위해 AWT는 Frame을 상속했고, Swing은 JFrame을 상속했다.
그리고 버튼은 AWT는 Button을, Swing은 JButton을 사용하고 있다.
이벤트 디스 패칭 스레드
Swing은 스레드에 안전하지 않기 때문에 작업 스레드들이 동시에 접근해서 UI를 변경하게 되면 문 제가 발생할 수 있다.
Swing은 이벤트 디스패칭 스레드(event-dispatching thread)에 의해 순차적으 로 UI 변경 작업을 진행하도록 설계되어 있다.
작업 스레드는 SwingUtilities 클래스의 invokeLater() 메소드를 이용해서 큐Queue에 Runnable 객체를 저장한다.
- 메소드 이름이 invokeLater 인 이유는 큐에 먼저 저장된 Runnable을 처리하고 나중에 처리한다는 의미
- 차이
스택 LIFO
큐 FIFO
invokeLater() 메소드를 호출하는 방법
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class App extends JFrame{
public App() {
// 제목 설정
setTitle("Swing App");
//윈도우 크기 설정
setSize(300, 100);
// 종료 버튼 처리
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String[] args) {
// 이벤트 큐에 Runnable 넣기 // 윈도우를 이용한 스레드 작업
SwingUtilities.invokeLater(new Runnable() {
public void run() {
App app = new App();
app.setVisible(true);
System.out.println(Thread.currentThread().getName());
}
});
}
}
- 실행 결과 -
AWT-EventQueue-0이 출력되었는데, 이것이 이벤트 디스패칭 스레드의 이름 이다.
Swing 컨테이너
컨테이너 클래스 | 용도 |
JDesktopPane | 내부 윈도우를 여러 개 포함할 수 있는 MDI 프로그램을 만들 때 사용 |
JDialog | 다이얼로그 윈도우를 만들 때 사용 |
JFrame | 작업 표시줄, 메뉴가 제공되는 윈도우를 만들 때 사용 |
JInternalFrame | JDesktopPane에 포함되는 내부 윈도우를 만들 때 사용 |
JPanel | 컴포넌트들을 배치할 때 사용 |
JScrollPane | 수직 또는 수평 스크롤이 필요할 때 사용 |
JSplitPane | 수직 또는 수평으로 보여주는 크기를 조절할 때 사용 |
JTabbedPane | 여러 가지 탭을 제공할 때 사용 |
JWindow | 작업 표시줄, 메뉴가 없는 윈도우를 만들 때 사용 |
Swing 컨테이너 구조
JWindow, JFrame, JDialog, JInternalFrame 컨테이너는 다른 컨테이너와 달리 기본 판(Root Pane) 여러 겹의 판(Pane)으로 구성되어 있다
1) GlassPane
GlassPane은 다른 패널 위에 존재하면서 기본적으로 숨겨져 있는 투명한 판이다.
2) JMenuBar와 ContentPane
JMenuBar는 메뉴가 포함되는 판이고, 그 아래 쪽에 있는 ContentPane은 버튼과 같은 UI 컴포넌트가 배치되는 판이다.
3) LayeredPane
여러 컴포넌트들이 겹쳐질 때 각 컴포넌트의 상하 위치를 결정한다
JWindow
- JWindow는 윈도우 경계선, 제목 표시줄, 메뉴바가 모두 없는 윈도우를 만드는 컨테이너
- 컴포 넌트만 배치할 수 있는 평면 공간만을 갖는다
- 게임 애플리케이션처럼 제목 표시줄이 없는 윈도우를 만들 때 주로 이용된다
public class JWindowExample extends JWindow {
public JWindowExample() {
this.setSize(450, 300);
}
}
- 윈도우는 반드시 폭width과 높이height가 있어야 하기 때문에 생성자에서 setSize() 메소드로 폭과 높 이를 주면 된다
- 윈도우를 화면 중앙에 띄우기 위해서는 화면의 중앙 지점을 얻어서 윈도우의 좌측 상단 모서리의 좌표를 계산해야 한다
JWindow를 화면 중앙에 띄우기 위해 필요한 코드
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Point centerPoint = ge.getCenterPoint();
int leftTopX = centerPoint.x - getWidth()/2;
int leftTopY = centerPoint.y - getHeight()/2;
this.setLocation(leftTopX, leftTopY);
java.awt.GraphicsEnvironment는 그래픽 환경에 대한 정보를 가지고 있는 객체이다.
GraphicsEnvironment 객체는 정적(static) 메소드인 getLocalGraphicsEnvironment()를 호출해서 얻을 수 있다.
- GraphincsEnvironment의 getContentPoint() 메소드는 화면 중앙 지점의 X좌표와 Y좌표를 가지고 있는 Point 객체를 리턴
- 화면 중앙 좌표와 윈도우 폭, 높이로 JWindow 의 좌측 상단 모서리 좌표를 계산
- JWindow의 setLocation() 메소드로 좌 측 상단 모서리 좌표를 설정
JWindowExample jWindow = new JWindowExample();
jWindow.setVisible(true);
- 반대로 setVisible(false)을 호출하면 JWindow가 화면에서 사라짐
- 은 JWindow가 화 면에서 완전히 제거되는 것이 아니라 단지 숨겨짐
- 다시 setVisible(true)을 호출하면 언제 든지 나타남
- JWindow를 화면에서 완전히 제거하고 싶다면 dispose() 메소드를 호출
jWindow.dispose();
JWindow를 이용해서 게임을 시작할 때 보여줄 로고 윈도우
import java.awt.BorderLayout;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
public class JWindowExample extends JWindow {
public JWindowExample() {
//JWindow의 크기
this.setSize(600, 350);
//JWindow를 화면 중앙으로 띄우기
// 사용자의 화면 해상도 가져오기
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Point centerPoint = ge.getCenterPoint();
int leftTopX = centerPoint.x - this.getWidth()/2;
int leftTopY = centerPoint.y - this.getHeight()/2;
this.setLocation(leftTopX, leftTopY);
//JWindow에 이미지가 포함된 JLabel 추가
JLabel label = new JLabel();
label.setIcon(new ImageIcon(getClass().getResource("game.png")));
getContentPane().add(label, BorderLayout.CENTER);
//마우스 클릭 이벤트 처리
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
dispose();
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JWindowExample jWindow = new JWindowExample();
jWindow.setVisible(true);
}
});
}
}
- 실행 결과 -
화면 중앙에 로고가 뜸.
JFrame
JFrame은 JWindow와는 달리 윈도우 경계선, 제목 표시줄, 메뉴바가 있는 윈도우를 만드는 컨테 이너 클래스이다.
setDefaultCloseOperation() 메소드에 지정할 수 있는 종료 버튼의 기능별 매개값은 다음 네 가지 종류가 있다.
기능별 상수 | 설명 |
WindowConstants.DO_NOTHING_ON_CLOSE | 아무 것도 하지 않음 |
WindowConstants.HIDE_ON_CLOSE | 화면에서 JFrame 숨김 (기본) |
WindowConstants.DISPOSE_ON_CLOSE | 화면에서 JFrame 완전히 제거, 다른 JFrame이 없다면 윈도우 프로세스를 종료 |
JFrame.EXIT_ON_CLOSE | 윈도우 프로세스를 종료 |
JFrame으로 제목 표시줄이 있는 윈도우를 띄우고 종료하는 방법
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class JFrameExample extends JFrame{
public JFrameExample() {
// JWindow의 크기
this.setSize(600, 500);
// 제목 표시줄 내용
this.setIconImage(new ImageIcon(getClass().getResource("icon.png")).getImage());
this.setTitle("메인창");
// 종료 버튼의 기본 기능
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// JWindow를 화면 중앙으로 띄우기
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Point centerPoint = ge.getCenterPoint();
int leftTopX = centerPoint.x - this.getWidth()/2;
int leftTopY = centerPoint.y - this.getHeight()/2;
this.setLocation(leftTopX, leftTopY);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrameExample jFrame = new JFrameExample();
jFrame.setVisible(true);
}
});
}
}
- 실행 결과 -
화면 중앙에 메인창 위치
JTabbedPane
- 탭tab별로 다른 내용을 보여주 기 위해 사용되는 컨테이너이다.
- 독립적인 윈도우 모양을 갖고 있지 않기 때문 에 JWindow, JFrame, JDialog 등과 같은 최 상위 레벨 컨테이너에 배치
두 개의 탭을 추가한 다음 각 탭을 클릭하면 두 개의 JPanel이 이미지를 교체
import java.awt.BorderLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class JTabbedPaneExample extends JFrame {
private JTabbedPane jTabbedPane;
private JPanel tab1Panel;
private JPanel tab2Panel;
//메인 윈도우 설정
public JTabbedPaneExample() {
this.setTitle("JTabbedPaneExample");
this.setSize(300, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.getContentPane().add(getJTabbedPane(), BorderLayout.CENTER);
}
//JTabbedPane 생성 및 Tab 추가
private JTabbedPane getJTabbedPane() {
if (jTabbedPane == null) {
jTabbedPane = new JTabbedPane();
jTabbedPane.setTabPlacement(JTabbedPane.LEFT);
jTabbedPane.addTab("탭1", getTab1Panel());
jTabbedPane.addTab("탭2", getTab2Panel());
}
return jTabbedPane;
}
//Tab1에 추가된 JPanel 생성
private JPanel getTab1Panel() {
if(tab1Panel == null) {
tab1Panel = new JPanel();
JLabel jLabel = new JLabel();
jLabel.setIcon(new ImageIcon(getClass().getResource("duke1.gif")));
tab1Panel.add(jLabel);
}
return tab1Panel;
}
//Tab2에 추가될 JPanel 생성
private JPanel getTab2Panel() {
if(tab2Panel == null) {
tab2Panel = new JPanel();
JLabel jLabel = new JLabel();
jLabel.setIcon(new ImageIcon(getClass().getResource("duke2.gif")));
tab2Panel.add(jLabel);
}
return tab2Panel;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JTabbedPaneExample jFrame = new JTabbedPaneExample();
jFrame.setVisible(true);
}
});
}
}
- 실행 결과 -
JScrollPane
JScrollPane은 포함된 컴포넌트의 크기가 JScrollPane 자신보다 큰 경우 수평 또는 수직 스크롤 바를 이용해서 볼 수 있게 해준다.
JScrollPane은 다른 컨테이너와는 달리 단 하나의 컴포넌트만을 포함시킬 수 있다.
- 그림을 보면 JScrollPane은 JFrame의 중앙에 위치하고 있다.
- JScrollPane안에는 그림을 포함 하고 있는 JLabel 배치되어 있는데, 그림의 크기가 JScrollPane보다 크기 때문에 수직 및 수평 스 크롤이 생긴다
- 스크롤이 필요한 컴포넌트에는 큰 내용을 포함하고 있는 JLabel, JTextArea, JList, JTable, JTree 등이 있다
JLabel에 큰 이미지를 넣고, JScrollPane에 JLabel을 추가
JFrame 중앙에 JScrollPane을 배치
JFrame의 사이즈가 이미지보다 작기 때문에 스크롤 이 자동 생성
import java.awt.BorderLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class JScrollPaneExample extends JFrame{
private JScrollPane scrollImage;
private JLabel lblImage;
public JScrollPaneExample() {
this.setTitle("JScrollPaneExample");
this.setSize(350, 230);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.getContentPane().add(getScrollImage(), BorderLayout.CENTER);
}
private JScrollPane getScrollImage() {
if (scrollImage == null) {
scrollImage = new JScrollPane(getLblImage());
}
return scrollImage;
}
//JLabel 생성
public JLabel getLblImage() {
if(lblImage == null) {
lblImage = new JLabel();
lblImage.setIcon(new ImageIcon(getClass().getResource("snow.jpg")));
}
return lblImage;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JScrollPaneExample jFrame = new JScrollPaneExample();
jFrame.setVisible(true);
}
});
}
}
- 실행 결과 -