1. 이벤트 기반 프로그래밍

  • 이벤트 기반 프로그래밍(Event Driven Programming)

    • 사용자의 입력(마우스 클릭, 키보드 입력 등)이나 센서 입력, 다른 프로그램의 메시지와 같은 ‘이벤트’가 발생했을 때, 프로그램의 흐름이 결정되는 방식
    • 이벤트가 발생하면 해당 이벤트를 처리하기 위해 미리 등록된 ‘이벤트 리스너’가 실행
  • 이벤트의 종류

    • 사용자 입력: 마우스 드래그, 클릭, 키보드 누름 등
    • 센서, 네트워크, 다른 프로그램으로부터의 데이터 및 메시지
  • 이벤트 처리 순서

    1. 이벤트 발생 (예: 사용자가 마우스 버튼 클릭)
    2. 이벤트 객체 생성 (발생한 이벤트의 정보를 담음)
    3. 이벤트 리스너 탐색 및 호출 (이벤트 소스에 등록된 리스너를 찾음)
    4. 이벤트 리스너 실행 (이벤트 객체를 인자로 받아 처리)
  • 이벤트 관련 용어

    • 이벤트 소스: 이벤트를 발생시킨 GUI 컴포넌트
    • 이벤트 객체: 발생한 이벤트에 대한 정보
    • 이벤트 리스너: 컴포넌트에 등록된 이벤트를 처리하는 코드
    • 이벤트 분배 스레드: 무한 루프를 실행하는 스레드

2. 이벤트 객체

  • 이벤트 객체

    • 이벤트가 발생했을 때, 해당 이벤트에 대한 상세 정보를 담고 있는 객체
    • 이 객체는 이벤트 리스너에게 전달되어 이벤트 처리 로직에 사용
  • 포함된 정보

    • 이벤트 종류 및 소스 (이벤트를 발생시킨 GUI 컴포넌트)
    • 화면 및 컴포넌트 내의 좌표 (마우스 이벤트의 경우)
    • 클릭된 마우스 버튼 종류, 클릭 횟수
    • 눌러진 키의 코드 값 및 문자 값 (키보드 이벤트의 경우)
  • 주요 이벤트 객체

    • ActionEvent: 버튼 클릭, 메뉴 선택 등 액션이 발생했을 때
    • MouseEvent: 마우스 버튼 누름, 뗌, 클릭, 이동, 드래그 등
    • KeyEvent: 키보드의 키를 누르거나 뗄 때
    • ItemEvent: 체크박스나 라디오 버튼의 상태가 변경될 때
    • WindowEvent: 윈도우 창의 상태 변화(열기, 닫기, 활성화 등)가 있을 때

3. 이벤트 리스너

  • 이벤트 리스너(Event Listener)

    • 특정 이벤트가 발생했을 때 이를 처리하는 코드를 담고 있는 클래스
    • JDK는 이벤트 리스너 작성을 위해 인터페이스를 제공하며, 개발자는 이 인터페이스의 추상 메소드를 구현한다.
  • 주요 리스너 인터페이스

    • ActionListener: actionPerformed() 메소드를 구현하여 ActionEvent를 처리한다.
    • MouseListener: mousePressed(), mouseReleased(), mouseClicked() 등 5개의 메소드를 구현하여 마우스 이벤트를 처리한다.
    • KeyListener: keyPressed(), keyReleased(), keyTyped() 3개의 메소드를 구현하여 키보드 이벤트를 처리한다.
  • 작성 방법

    1. 독립 클래스: 별도의 파일로 리스너 클래스를 작성하며, 여러 곳에서 재사용할 때 유용하다.
    2. 내부 클래스(Inner class): 특정 클래스 내부에 멤버처럼 리스너 클래스를 작성하며, 해당 클래스에서만 사용할 때 적합하다.
    3. 익명 클래스(Anonymous class): 이름 없이 클래스를 정의하고 동시에 객체를 생성하는 방식으로, 코드가 간단할 때 유용하다.

3.1. 예제

  • 예제 10-1: 독립 클래스 방식
    • ActionListener를 구현하는 MyActionListener 클래스를 별도의 파일로 만든다.
    • 여러 GUI 컴포넌트에서 재사용이 필요할 때 유용한 구조이다.
public class IndepClassListener extends JFrame {
	public IndepClassListener() {
		// ...
	}
}
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");
	}
}
  • 예제 10-2: 내부 클래스 방식
    • ActionListener를 구현하는 MyActionListener 클래스를 GUI를 생성하는 JFrame 클래스 내부에 정의한다.
    • 외부 클래스의 멤버 변수나 메소드에 쉽게 접근할 수 있는 장점이 있다. 예제에서는 버튼의 텍스트로 프레임의 제목을 변경하는 코드가 추가되었다.
public class InnerClassListener extends JFrame {
	InnerClassListener() {
		// ...
	}
	private 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");
			// InnerClassListener의 멤버나 JFrame의 멤버를 호출할 수 있다.
			InnerClassListener.this.setTitle(b.getText());
			// 프레임 타이틀에 버튼 문자열을 출력한다.
		}
	}
}
  • 예제 10-3: 익명 클래스 방식
    • 클래스 이름 없이, 리스너를 추가하는 코드(addActionListener) 안에서 직접 클래스를 정의하고 객체를 생성한다.
    • 코드가 간결하며, 해당 이벤트 처리 로직이 한 번만 사용될 경우에 적합하다.
public class AnonymousClassListener extends JFrame {
	public AnonymousClassListener() {
		// ...
		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());
			}
		});
	}
}
  • 예제 10-4: MouseListener를 이용한 문자열 이동
    • MouseListener 인터페이스를 구현하여 마우스 이벤트를 처리한다.
    • mousePressed 메소드에서 마우스가 클릭된 좌표를 얻어와 JLabel의 위치를 변경한다.
    • MouseListener의 모든 추상 메소드(총 5개)를 반드시 구현해야 하므로, 사용하지 않는 메소드들은 빈 채로 둔다.
public class MouseListenerEx extends JFrame {
	public MouseListenerEx() {
		// ...
	}
	class MyMouseListener implements MouseListener {
		public void mousePressed(MouseEvent e) {
			int x = e.getX();
			int y = e.getY();
			la.setLocation(x, y);
		}
	}
}

4. Adapter 클래스

  • 어댑터(Adapter) 클래스

    • 리스너 인터페이스의 모든 추상 메소드를 빈 상태로 미리 구현해 놓은 클래스
    • MouseListener처럼 여러 메소드를 구현해야 하는 인터페이스의 경우, 어댑터 클래스를 상속받으면 원하는 메소드만 오버라이딩(재정의)하여 사용할 수 있어 코드 작성이 간결해진다.
  • 예시: MouseAdapterMouseListener, MouseMotionListener, MouseWheelListener 인터페이스를 구현한 클래스이다.

  • 주의: ActionListenerItemListener와 같이 추상 메소드가 하나뿐인 인터페이스는 대응하는 어댑터 클래스가 없다.

4.1. 예제

  • 예제 10-5: MouseAdapter를 이용한 리팩토링
    • 예제 10-4를 MouseAdapter 클래스를 상속받아 개선한 버전
    • MouseAdapterMouseListener의 메소드들이 미리 구현되어 있으므로, 필요한 mousePressed 메소드만 오버라이딩(재정의)하면 되어 코드가 더 간결해진다.
public class MouseAdapterEx extends JFrame {
	public MouseAdapterEx() {
		// ...
	}
	class MyMouseAdapter extends MouseAdapter {
		public void mousePressed(MouseEvent e) {
			int x = e.getX();
			int y = e.getY();
			la.setLocation(x, y);
		}
	}
}

5. KeyEvent와 KeyListener

키보드 입력 시 KeyEvent가 발생하며, 이를 처리하기 위해 KeyListener를 사용한다.

  • 포커스(Focus)

    • 컴포넌트가 키 이벤트를 받으려면 포커스를 가지고 있어야 한다.
    • component.setFocusable(true);component.requestFocus(); 코드를 사용하여 포커스를 설정할 수 있다.
  • 키의 종류

    • 유니코드 키
      • A-Z, 0-9 등 문자에 해당하는 키
      • keyPressed(), keyTyped(), keyReleased() 순서로 이벤트가 발생한다.
    • 유니코드가 아닌 키
      • F1, Home, Shift 등 제어 키
      • keyPressed(), keyReleased() 이벤트만 발생한다.
  • 입력된 키 판별

    • char KeyEvent.getKeyChar()
      • 입력된 문자의 유니코드 값을 리턴한다.
      • (문자 키에만 해당)
    • int KeyEvent.getKeyCode()
      • 모든 키에 대해 정의된 가상 키(Virtual Key) 코드를 리턴한다.
      • (예: KeyEvent.VK_F1, KeyEvent.VK_UP)
    • String KeyEvent.getKeyText()
      • 가상 키 코드에 해당하는 문자열을 리턴한다.
      • (예: “F1”, “Shift”)

5.1. 예제

  • 예제 10-6: 다양한 KeyEvent 정보 출력
    • 키보드를 누를 때마다 해당 키의 키 코드(getKeyCode), 유니코드 문자(getKeyChar), 그리고 키의 이름(getKeyText)을 화면의 레이블에 출력한다.
    • 이를 통해 키 이벤트로부터 어떤 정보를 얻을 수 있는지 보여준다.
public class KeyListenerEx extends JFrame {
	public KeyListenerEx() {
		// ...
	}
	class MyKeyListener extends KeyAdapter {
		public void keyPressed(KeyEvent e) {
			int keyCode = e.getKeyCode();
			char keyChar = e.getKeyChar();
			keyMessage[0].setText(Integer.toString(keyCode));
			keyMessage[1].setText(Character.toString(keyChar));
			keyMessage[2].setText(e.getKeyText(keyCode));
		}
	}
}
  • 예제 10-7: 특정 키 입력으로 배경색 변경

    • <F1> 키를 누르면 컨텐트팬의 배경색이 초록색으로, % 키를 누르면 노란색으로 변경된다.
    • <F1>과 같은 기능 키는 getKeyCode()로, %와 같은 문자 키는 getKeyChar()로 구분하여 처리하는 방법을 보여준다.
  • 예제 10-8: 방향키로 문자열 이동

    • 상(UP), 하(DOWN), 좌(LEFT), 우(RIGHT) 방향키를 이용하여 “HELLO” 문자열을 10픽셀씩 이동시킨다.
    • keyPressed 메소드 안에서 switch 문을 사용해 눌린 키의 코드(getKeyCode)를 확인하고 각 방향키에 맞는 위치 이동 로직을 수행한다.

6. MouseEvent와 MouseListener

마우스 조작에 따라 MouseEvent가 발생하며, 이를 처리하기 위해 MouseListenerMouseMotionListener를 사용한다.

  • 리스너 종류

    • MouseListener
      • 마우스 버튼 누름(mousePressed)
      • 마우스 버튼 뗌(mouseReleased)
      • 마우스가 컴포넌트를 클릭(mouseClicked)
      • 마우스가 컴포넌트에 진입(mouseEntered)
      • 마우스가 컴포넌트에서 이탈(mouseExited)
    • MouseMotionListener
      • 마우스가 이동하는 동안(mouseMoved)
      • 마우스가 드래그하는 동안(mouseDragged)
      • 마우스 휠이 구르는 동안(mouseWheelMoved)
  • 마우스 정보 얻기

    • getX(), getY(), getPoint()
      • 이벤트가 발생한 마우스 포인터의 위치를 얻는다.
    • getButton()
      • 눌러진 마우스 버튼을 판별한다. (예: MouseEvent.BUTTON1은 왼쪽 버튼)
    • getClickCount()
      • 마우스 클릭 횟수를 얻어 더블클릭 등을 감지할 수 있다.

6.1. 예제

  • 예제 10-9: 마우스 및 마우스 모션 이벤트 활용

    • MouseListenerMouseMotionListener를 모두 구현하여 다양한 마우스 이벤트를 처리한다.
    • 기능
      • 마우스가 컴포넌트 안으로 들어오면(mouseEntered) 배경색이 하늘색으로 변경된다.
      • 컴포넌트 밖으로 나가면(mouseExited) 배경색이 노란색으로 변경된다.
      • 마우스를 누르거나(mousePressed), 떼거나(mouseReleased), 드래그(mouseDragged), 이동(mouseMoved)할 때마다 현재 좌표를 레이블에 표시한다.
  • 예제 10-10: 더블클릭으로 배경색 변경

    • 마우스를 더블클릭할 때마다 컨텐트팬의 배경색이 랜덤한 색으로 변경된다.
    • mouseClicked 이벤트 내에서 getClickCount() 메소드의 반환 값이 2인지 확인하여 더블클릭을 감지한다.