본문 바로가기
학교 강의/자바프로그래밍

자바프로그래밍 총정리2 - 스윙, GUI, 이벤트

by hoshi03 2024. 6. 19.

• 자바 GUI 프로그래밍 방법

GUI 컴포넌트, 그래픽

awt 패키지, swing 패키지 사용

 

• AWT <- java.awt 패키지

자바 처음부터 배포된 GUI 라이브러리 java.awt 패키지

AWT 컴포넌트는 중량 컴포넌트, 운영체제에 도움을 받아 작동

 

•Swing <- javax.swing 패키지

AWT 기반 순수 자바 언어로 만들어진 라이브러리, 클래스가 J로 시작 

Swing 컴포넌트는 경량 컴포넌트, 운영체제에 의존하지 않는다

JComponent를 상속받는 대부분의 스윙 컴포넌트, AWT의 Container를 상속받는 몇개의 클래스가 있다

-

• Swing 에서 컨테이너와 컴포넌트

컨테이너 : 다른 GUI 컴포넌트를 포함 가능, 다른 컨테이너에 포함 가능

최상위 컨테이너 : 다른 컨테이너에 속하지 않고 독립적으로 화면 출력 가능한 컨테이너 JFrame, JDialog, JApplet

모든 컴포넌트는 컨테이너에 포함되어야 화면에 출력 가능

 

컴포넌트 : 컨테이너에 포함되어야 화면에 출력 가능

 

• 스윙 GUI 프로그램 만들기

스윙 프레임을 작성해서 main에서 호출

import javax.swing.*;

public class Test extends JFrame {
    public Test() {
    setTitle("300x300 스윙 프레임 만들기"); setSize(300,300); // 프레임 크기 300x300
    setVisible(true); // 프레임 출력
    }

    public static void main(String[] args) {
        Test frame = new Test();
    }
}

 

스윙 프로그램 종료

Sysyem.exit(0); <- 무조건 종료

프레임 종료 버튼 (X) 모양 버튼을 클릭하면 어떤 일이 일어나나?

- 프레임을 종료해서 프레임 윈도우가 닫히지만 응용프로그램이 종료된 것이 아니라 안보이게 된 것

- 다시 setVisible(true)를 호출하면 이전처럼 작동한다

! 프레임 종료 버튼을 클릭할 떄 프레임을 닫고 응용프로그램을 종료하는 방법

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

 위의 명령어로 완전히 종료시킨다

 

 스윙 프로그램이 실행되는 동안 생성되는 스레드

스윙 프로그램이 실행되는 동안 main()을 실행하는 메인 스레드와 스윙 프로그램을 실행할때 자동 실행되는 이벤트 분배 스레드(GUI 화면 그리기, 키/마 입력 받아서 이벤트 처리)가 자동으로 실행된다

스윙 프로그램은 종료시키지 않으면 메인 스레드가 종료되어도 이벤트 분배 스레드가 살아있어 화면 그리기나 키/마 입력을 받는다

 

• 컨테이너와 배치 개념

컨테이너마다 하나의 배치 관리자가 있어서 삽입되는 컴포넌트의 위치, 크기를 결정해서 배치한다

 

JPanel p = new JPanel();
p.setLayout(new BorderLayout());

 

 레이아웃 4개

flowlayout - 좌->우, 위->아래 방향으로 배치

수평30, 수직 40픽셀 간격, left 정렬 배치 flowLayout

c.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 40));
c.add(new JButton("add"));
c.add(new JButton("sub"));
c.add(new JButton("mul"));
c.add(new JButton("div"));
c.add(new JButton("Calculate"));

 

borderlayout - 컨테이너를 5분할, 동,서,남,북,center

수평 30픽셀, 수직 20픽셀의 간격을 가지는 borderlayout

Container c = getContentPane();
c.setLayout(new BorderLayout(30, 20));
c.add(new JButton("Calculate"), BorderLayout.CENTER);
c.add(new JButton("add"), BorderLayout.NORTH); 
c.add(new JButton("sub"), BorderLayout.SOUTH); 
c.add(new JButton("mul"), BorderLayout.EAST);
c.add(new JButton("div"), BorderLayout.WEST);

 

gridlayout - 컨테이너 공간을 동일한 사각형 그리드로 분할, 각 셀에 하나의 컴포넌트 배치

new GridLayout(행,열,수평간격,수직간격)

GridLayout grid = new GridLayout(4, 2);
grid.setVgap(5);
Container c = getContentPane(); 
c.setLayout(grid);
c.add(new JLabel(" 이름"));
c.add(new JTextField(""));
c.add(new JLabel(" 학번"));
c.add(new JTextField(""));
c.add(new JLabel(" 학과"));
c.add(new JTextField(""));
c.add(new JLabel(" 과목"));
c.add(new JTextField("")); setSize(300, 200);
setVisible(true);

 

CardLayout <- 겹치는 레이아웃 같은데 교재에서 안다룸

 

• 배치관리자 없는 컨테이너

응용프로그램에서 컴포넌트의 절대 크기와 절대 위치를 결정

 

사용하는 경우

컴포넌트의 크기와 위치를 개발자가 임의로 결정

게임처럼 컴포넌트의 위치,크기가 수시로 변하는 경우

컴포넌트 겹치는 경우

 

기존 배치 관리자를 제거하고 사용자가 알아서 배치한다

container.setLayout(null); <- 기존 배치관리자 제거

컴포넌트 크기 설정 : component.setSize(int width, int height);
컴포넌트 위치 설정 : component.setLocation(int x, int y);
컴포넌트 위치와 크기 동시 설정 : component.setBounds(int x, int y, int width, int height);

 

- 사용자가 원하는 위치에 컴포넌트 배치하는 예시

import javax.swing.*;
import java.awt.*;
public class NullContainerEx extends JFrame {
    public NullContainerEx() {
        setTitle("Null Container Sample"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container c = getContentPane();
        c.setLayout(null);
        
        JLabel la = new JLabel("Hello, Press Buttons!");
        la.setLocation(130, 50);
        la.setSize(200, 20);
        c.add(la);
        
        for(int i=1; i<=9; i++) {
            JButton b = new JButton(Integer.toString(i)); // 버튼 생성
            b.setLocation(i*15, i*15);
            b.setSize(50, 20);
            c.add(b); // 버튼을 컨텐트팬에 부착 }
            setSize(300, 200);
            setVisible(true);
        }
        
        public static void main(String[] args) {
            new NullContainerEx();
        }
    }
}

 

 

• 자바 이벤트 처리

이벤트 기반 프로그래밍

이벤트 종류 - 키/마, 센서, 네트워크 송수신, 다른 프로그램이나 스레드로부터 메세지

이벤트가 발생하면 이벤트 리스너 실행 <-> 배치 실행(개발자가 프로그램의 흐름 결정)

 

• 이벤트 처리 순서

1 이벤트 발생

2 이벤트 객체 생성

3 이벤트 리스너 찾아서 호출, 이벤트 객체가 이벤트 리스너에 전달

4 이벤트 리스너 실행

 

• 이벤트 관련 용어

이벤트 소스 - 이벤트를 발생시킨 gui 컴포넌트

이벤트 객체 - 발생한 이벤트 정보(이벤트 종류,소스,좌표,버튼 종류, 눌러진 키 등등), 이벤트 리스너에 전달됨

이벤트 리스너 - 이벤트 처리 코드, 컴포넌트에 등록되어야 작동 가능

이벤트 분배 스레드 - jvm으로 부터 이벤트 발생을 통지받고 이벤트 소스,종류 결정, 리스너 호출

 

- 이벤트 리스너 : 이벤트 처리하는 코드, 클래스로 작성

개발자가 jdk에 있는 이벤트 리스너 인터페이스 추상 메서드를 구현해서 사용

ActionListner, MouseListener 등 인터페이스 추상 메서드를 구현해서 사용한다

 

컴포넌트는 여러 이벤트에 대한 리스너를 동시에 가지고, 한 이벤트에 대한 여러 리스너를 동시에 가질 수 있다

이때 리스너는 등록된 반대 순으로 모두 실행된다 

 

• 이벤트 리스너 작성 방법

1. 독립 클래스 - 이벤트 리스너를 클래스로 작성해서 여러 곳에서 가져다가 사용

2. 내부 클래스 - 특정 클래스에서 이벤트 리스너가 필요할때 클래스 멤버처럼 작성

3. 익명 클래스 - 클래스로 만들지 않을 만큼 간단한 리스너를 작성할때 사용

 

- 독립 클래스로 action 이벤트 

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Test extends JFrame {
    public Test() {
        setTitle("Action 이벤트 리스너 예제");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container c = getContentPane();
        c.setLayout(new FlowLayout());
        JButton btn = new JButton("action");
        btn.addActionListener(new MyActionListener()); // Action 리스너 달기
        c.add(btn);
        setSize(350, 150);
        setVisible(true); }
    public static void main(String [] args) {
        new Test();
    }
}

// 이벤트 리스너 독립 클래스
class MyActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
    JButton b = (JButton) e.getSource();
    if (b.getText().equals("Action")) b.setText("액션");
    else b.setText("Action");
    }
}

 

- 내부 클래스 action 이벤트

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Test extends JFrame {
    public Test() {
        setTitle("Action 이벤트 리스너 예제");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container c = getContentPane();
        c.setLayout(new FlowLayout());
        JButton btn = new JButton("action");
        btn.addActionListener(new InnerActionListner()); // Action 리스너 달기
        c.add(btn);
        setSize(350, 150);
        setVisible(true);
    }

    // 내부 클래스로 만들기
    private class InnerActionListner implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            JButton b = (JButton) e.getSource();
            if (b.getText().equals("Action")) b.setText("액션");
            else b.setText("Action");
            Test.this.setTitle(b.getText());
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
    }
}

 

• 익명 클래스로 이벤트 리스너 작성하기

클래스 정의 + 인스턴스 생성을 한번에 작성

 

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Test extends JFrame {
    public Test() {
        setTitle("Action 이벤트 리스너 작성");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container c = getContentPane();
        c.setLayout(new FlowLayout());
        JButton btn = new JButton("Action");
        c.add(btn);
        
        //익명 클래스 작성, btn을 클릭했을 때 이벤트니 btn에 이벤트 리스너를 추가
        btn.addActionListener(new ActionListener() {
            public void actionPerformed (ActionEvent e) {
                JButton b = (JButton)e.getSource();
                if(b.getText().equals("Action")) b.setText("액션");
                else b.setText("Action");
                setTitle(b.getText());
            }
        });

        setSize(350, 150);
        setVisible(true);
    }
    public static void main(String [] args) {
        Test test = new Test();
    }
}

 

• 마우스 이벤트

마우스 클릭 이벤트, 휠 이벤트, 드래그 이벤트 등등

addMouseListener

addMouseMotionListener

MouseWheelListener

-아래같은 마우스리스너 클래스를 만든 다음 사용할 클래스 컨테이너에 addMouseListener(MyMouseListener())로 추가

class MyMouseListener implements MouseListener {
    public void mousePressed(MouseEvent e) {
    int x = e.getX();
    int y = e.getY();
    la.setLocation(x, y);
    }
    public void mouseReleased(MouseEvent e) {}
    public void mouseClicked(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}

    public static void main(String [] args) {
    new MouseListenerEx();
    }
}

 

• 어댑터 클래스

이벤트 리스너 작성시 사용하지 않는 추상 메서드를 모두 구현해야 하는 부담을 줄이기 위해서

위의 리스너에 public void mouseReleased(MouseEvent e) {} 형태로 안쓰는 메서드를 단순 리턴하게 구현

- 추상 메서드가 하나뿐이면 그건 구현해야하니 어뎁터가 없다

 

- 어댑터를 상속받아서 이용하면 위의 리스너와 같은 기능을 하는 코드가 훨씬 깰끔해진다

class MyMouseAdapter extends MouseAdapter { 
    public void mousePressed(MouseEvent e) {
    int x = e.getX(); 
    int y = e.getY(); 
    la.setLocation(x, y);
    } 
}

 

 포커스

포커스는? 컴포넌트나 프로그램이 키 이벤트를 독점하는 권한

- 컴포넌트에 포커스 설정 방법

component.setFocusable(true); // component가 포커스를 받을 수 있도록 설정 
component.requestFocus(); // componen에 포커스 강제 지정

 

- 스윙 프레임에 포커스 주는 방법

setVisile(true)로 스윙 프레임이 보이게 한 후에 위의 코드로 포커스 주기

 

- 마우스로 클릭한 컴포넌트에 포커스 주기

component.addMouseListener(new MouseAdapter() { 
	public void mouseClicked(MouseEvent e) {
        Component c = (Component)e.getSource(); // 클릭된 컴포넌트 
        c.setFocusable(true);
        c.requestFocus(); 
	}
}

 

• 키 리스너 & 이벤트

키 누르는 순간 keyPressed(KeyEvent e)

누른 키 떼는 순간 KeyRealeased(~)

누른 키 떼는 순간(유니코드)  KeyTyped(~) 3가지

키 이벤트를 받을 수 있는 조건 -> 포커스를 가진 컴포넌트

 

- 키 종류 2가지

유니코드 - 문자 알파벳, 특문 등

유니코드 아닌거 - Function키, Home키 등

! 모든 키에는 가상 키 코드 값이 있기에 가상 키 값으로 어떤 키인지 비교 판단 가능

 

- 입력된 키 판별

유니코드 알아내기 char KeyEvent.getKeyChar()

가상 키 값 알아내기 int KeyEvent.getKeyCode()

키 이름 알아내기 String KeyEvent.getKeyText(int keyCode)

 

• 컴포넌트 기반 GUI

 

JLabel textLabel = new JLabel("사랑합니다"); <- 단순 텍스트

 

- 이미지 넣기 ImageIcon 

ImageIcon image = new ImageIcon("images/sunset.jpg");

JLabel imageLabel = new JLabel(image);