자바의 이벤트 처리(프로그래밍 심화)(명품 JAVA Programming )
용어정리
본문정리
이벤트 기반 프로그래밍
이벤트 기반 프로그래밍은 이벤트의 발생에 의해 프로그램 실행 흐름이 결정되는 방식의 프로그래밍 패러다임이다. 이벤트는 키 입력, 마우스 클릭 등이다. 이벤트 기반 프로그래밍과 대조되는 방식은 프로그램 작성자에 의해 프로그램의 흐름이 결정되는 방식이다. 이벤트 기반 응용프로그램은 이벤트 리스너라는 각 이벤트를 처리하는 것을 보유하며 이벤트가 발생할 때마다 리스너가 실행된다. 또한 이벤트를 처리하는 프로그램 코드로서 컴포넌트에 연결되어야 작동한다. 각 컴포넌트들은 하나의 이벤트 리스너를 가지고 있다.
이하는 이벤트 관련 용어이다.
이벤트 소스 : 이벤트를 발생시킨 GUI 컴포넌트이다.
이벤트 객체 : 발생한 이벤트에 대한 정보(이벤트 종류, 이벤트 소스, 화면 좌표, 마우스 버튼 종류, 눌러진 키)를 담는 객체로서, 이벤트에 따라 서로 다른 정보가 저장된다.
이벤트 리스너 : 이벤트를 처리하는 코드로서 컴포넌트에 등록되어야 작동 가능하다.
이벤트 분배 스레드 : 이벤트 기반 프로그래밍의 핵심 요소로서 무한 루프를 실행하는 스레드이다. 자바 가상 기계로부터 이벤트의 발생을 통지 받아, 이벤트 소스와 이벤트 종류를 결정하고 이에 따라 적절한 이벤트 객체를 생성하고 이벤트 리스너를 찾아 호출한다.
이벤트 객체
이벤트 객체는 현재 발생한 이벤트에 관한 정보를 가진 객체이며 이벤트 리스너에게 전달된다.
이벤트 객체는 발생한 이벤트에 따라 다르지만 다음의 정보를 담는다.
이벤트 종류, 이벤트 소스, 화면 내 이벤트가 발생한 마우스 좌표, 컴포넌트 내 이벤트가 발생한 마우스 좌표, 버튼이나 메뉴 아이템에 이벤트가 발생한 경우 버튼이나 메뉴 아이템의 문자열, 클릭된 마우스 버튼 번호, 마우스 클릭 획수, 키가 눌러졌다면 키의 코드값과 문자값, 체크박스·라디오버튼에 이벤트가 발생하였다면 체크 상태
이벤트 객체는 메소드를 통해 이벤트 정보를 제공한다.
이벤트 리스너에서 가장 많이 사용되는 getSource() 메소드에 대해 설명하자면 현재 발생한 이벤트의 소스 컴포넌트의 레퍼런스를 리턴한다.
이하는 이벤트 객체와 이벤트 소스에 대해 정리한 것으로 이벤트 소스를 상속받은 컴포넌트에 대해 해당 이벤트가 발생가능하다.
ActionEvent | JButton |
JMenuItem | |
JTextField | |
ItemEvent | JCheckBox |
JRadioButton | |
JCheckBoxMenuItem | |
ListSelectioneEvent | JList |
KeyEvent | Component |
MouseEvent | Component |
FocusEvent | Component |
WindowEvent | Window |
AdjustmentEvent | JScrollBar |
ComponentEvent | Component |
ContainerEvent | Container |
이벤트 리스너
이벤트 리스너란 이벤트를 처리하는 자바 프로그램 코드로서 클래스로 만든다. 개발자는 JDK에서 제공되는 인터페이스를 상속받고 추상 메소드를 모두 구현하여 이벤트 리스너를 작성한다.
리스너 인터페이스들은 java.awt.event 패키지에 구현되어있다.
리스너는 3단계로 작성한다.
1. 이벤트와 이벤트 리스너 선택 : 목적에 적합한 이벤트와 리스너 인터페이스 선택
2. 이벤트 리스너 클래스 작성 : 리스너 인터페이스를 상속받는 클래스를 작성하고 추상 메소드 모두 구현
3. 이벤트 리스너 등록 : 이벤트를 받을 스윙 컴포넌트에 이벤트 리스너 등록
이벤트 리스너는 컴포넌트에 등록되어야 이벤트가 발생할 때 호출된다. 그리고 모든 컴포넌트가 모든 이벤트를 처리할 수 있는 것은 아니다. 어떤 컴포넌트가 어떤 이벤트 리스너를 가질 수 있는지 판단할 수 있어야 한다.
리스너 작성 방법에는 3가지가 있다.
독립 클래스로 이벤트 리스너 작성
내부 클래스로 이벤트 리스너 작성
익명 클래스로 이벤트 리스너 작성
이중 독립 클래스 방식은 리스너 코드가 길때 적합하나 다른 클래스의 맴버 접근이 어렵다.
내부 클래스 방식은 외부 클래스의 맴버 접근이 쉽다.
익명 클래스 방식은 내부 클래스 일종이라 외부 클래스의 맴버 접근이 쉬우며, 코드가 짧고 메소드가 적을 경우 유리하지만 코드가 길면 가독성이 떨어진다.
어뎁터 클래스
어뎁터 클래스는 리스너 인터페이스를 미리 구현해 놓은 클래스를 제공한다. 이는 인터페이스를 상속 받았을 때 사용하지 않을 메소드 또한 선언해야 하는 귀찮음을 덜어준다는 의미이다. 이하는 리스너 인터페이스와 대응하는 어댑터 클래스이다.
ActionListener | 없음 |
ItemListener | 없음 |
KeyListener | KeyAdaper |
MouseListener | MouseAdapter |
MouseMotionListener | MouseMotionAdapter 혹은 MouseAdapter |
FocusListener | FocusAdapter |
WindowListener | WindowAdapter |
AdjustmentListener | 없음 |
ComponentListener | ComponentAdapter |
ContainerListener | ContainerAdapter |
KeyEvent와 KeyListener
Key 이벤트는 사용자가 키를 입력할 때 발생하는 이벤트이며, 모든 컴포넌트가 key 이벤트를 받을 수 있다.그러나 응용프로그램 내에 포커스를 가진 컴포넌트가 키 입력을 독점한다. 여기서 포커스란 키 입력 독점권을 뜻한다. 스윙 응용프로그램에서는 강제로 임의의 컴포넌트에 포커스를 주기 위해 다음 2 코드가 필요하다.
component.setFocusable(true); component가 포커스를 받을 수 있도록 설정한다.
component.requestFocus(); component에게 포커스를 주어 키 입력을 받을 수 있게 한다.
해당 코드는 setVisibel 이후에 선언해야 한다. JFrame의 setVisible 메소드는 프레임을 출력한 후 포커스를 임의로 움직이기 때문이다.
또한 사용자가 마우스로 컴포넌트를 클릭하면( (component)e.getSource() ) 포커스를 얻도록하는 방법도 있다.
KeyListener 인터페이스는 키를 누르는 순간(keyPressed), 누른 키를 떼는 순간(keyReleased), 유니코드인 경우에 키를 떼는 순간(keyTyped)인 3개의 추상메소드로 구성되어 있다.
입력된 키를 판별하는 것엔 2가지 방법이 사용된다.
char keyEvent.getKeyChar() : 입력된 키의 유니코드 값을 리턴하며, 유니코드가 아닌 경우 KeyEvent.CHAR_UNDEFINED를 리턴한다.
int KeyEvent.getKeyCode() : 유니코드 키를 포함한 모든 키에 대해 정수형의 키 코드 값을 리턴한다. 키 코드는 운영체제, 하드웨어 따라 다르므로 getkeycode와 virtualkey 값을 비교해야 한다.
MouseEvent와 MouseListener, MouseMotionListener, MouseWheelListener
Mouse 이벤트는 사용자의 마우스 조작에 따라 총 8가지 경우에 따라 발생한다. 이하는 이벤트가 발생하는 경우의 리스너와 메소드이다.
Mouser 이벤트가 발생하는 경우 | 리스너의 메소드 | 리스너 |
마우스가 컴포넌트 위에 올라갈 때 | void mouseEntered(MouseEvent e) | MouseListener |
마우스가 컴포넌트에서 내려올 때 | void mouseExited(MouseEvent e) | MouseListener |
마우스 버튼이 눌러졌을 때 | void mousePressed(MouseEvent e) | MouseListener |
눌러진 버튼이 떼어질 때 | void mouseReleased(MouseEvent e) | MouseListener |
마우스로 컴포넌트를 클릭하였을 때 | void mouseClicked(MouseEvent e) | MouseListener |
마우스가 드래그 되는 동안 | void mouseDragged(MouseEvent e) | MouseMotionListener |
마우스가 움직이는 동안 | void mouseMoved(MouseEvent e) | MouseMotionListener |
마우스 휠이 구르는 동안 | void mouseWheelMoved(MouseEvent e) | MouseWheelListener |
마우스가 눌러진 위치에서 그대로 떼면 다음 순서로 메소드가 호출된다.
mousePressed() → mouseReleased() → mouseClicked()
마우스가 드래그되면 다음 순서로 호출된다.
mousePressed() → mouseDragged() → ······ → mouseDragged() →mouseReleased()
MouseEvent 객체는 Mouse 이벤트나 MouseMotion 이벤트 정보를 제공하는 객체이다.
마우스 클릭 횟수는 int getClickCount()이며 마우스 버튼은 int getButton()이다. 마우스 버튼은 NOBUTTON, BUTTON1, BUTTON2, BUTTON3 중 하나이다. 왼쪽 버튼은 BUTTON1이고 오른쪽 버튼은 BUTTON3이다.
본질문
Q1. JLavel 컴포넌트는 Mouse 이벤트를 받을 수 있다. JLabel 컴포넌트에 마우스를 올리면 "Love Java"가, 내리면 "사랑해"가 출력되도록 스윙 응용프로그램을 작성하라.
해답
package gui;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.math.*;
public class problem extends JFrame{
JLabel label = new JLabel("사랑해");
public problem() {
super.setTitle("예제");
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = super.getContentPane();
c.setLayout(new FlowLayout());
label.addMouseListener(new mo());
c.add(label);
super.setSize(350, 300);
super.setVisible(true);
}
class mo extends MouseAdapter{
public void mouseEntered(MouseEvent e) {
label.setText("Love Java");
}
public void mouseExited(MouseEvent e) {
label.setText("사랑해");
}
}
public static void main(String [] args) {
problem mf = new problem();
}
}
밑에는 문제 잘못 읽어서 휠로 푸는 경우
package gui;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.math.*;
public class problem extends JFrame{
JLabel label = new JLabel("Love Java");
public problem() {
super.setTitle("예제");
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = super.getContentPane();
c.setLayout(new FlowLayout());
c.add(label);
c.addMouseWheelListener(new mo());
super.setSize(350, 300);
super.setVisible(true);
}
class mo implements MouseWheelListener{
public void mouseWheelMoved(MouseWheelEvent e) {
int n = e.getWheelRotation();
if(n<0) {
label.setText("Love Java");
}
else {
label.setText("사랑해");
}
}
}
public static void main(String [] args) {
problem mf = new problem();
}
}
Q2. 컨텐트팬의 배경색은 초록색으로 하고 마우스를 드래깅하는 동안만 노란색으로 유지하는 스윙 응용프로그램을 작성하라.
package gui;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.math.*;
public class problem extends JFrame{
public problem() {
super.setTitle("예제");
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = super.getContentPane();
c.setLayout(null);
c.setBackground(Color.green);
c.addMouseMotionListener(new mo2());
c.addMouseListener(new mo1());
super.setSize(350, 300);
super.setVisible(true);
}
class mo2 extends MouseAdapter{
public void mouseDragged(MouseEvent e) {
Container ct = (Container)e.getSource();
ct.setBackground(Color.yellow);
}
}
class mo1 extends MouseAdapter{
public void mouseReleased(MouseEvent e){
Container ct = (Container)e.getSource();
ct.setBackground(Color.green);
}
}
public static void main(String [] args) {
problem mf = new problem();
}
}
Q3. JLabel을 활용하여 "Love Java"를 출력하고 왼쪽 화살표 키 (<Left> 키)를 입력할 때마다 "avaJ evoL"와 "Love Java"를 번갈아 출력하는 스윙 프로그램을 작성하라. StringBuffer 클래스의 reverse() 메소드를 이용하여 구현하는 것과 JLabel에 포커스를 설정하는 것을 잊지 말아야 한다.
package problem;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class chapter10 extends JFrame {
JLabel label = new JLabel("Love Java");
int n = 0;
public chapter10() {
super.setTitle("Left 키로 문자열 교체");
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = super.getContentPane();
c.setLayout(new FlowLayout());
c.addKeyListener(new key());
c.add(label);
super.setSize(350, 200);
super.setVisible(true);
c.setFocusable(true);
c.requestFocus();
}
class key extends KeyAdapter{
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();
if(keycode == KeyEvent.VK_LEFT && n == 0) {
label.setText("avaJ evoL");
n = 1;
}
else if(keycode == KeyEvent.VK_LEFT && n == 1) {
label.setText("Love Java");
n = 0;
}
}
}
public static void main(String args[]) {
chapter10 hello = new chapter10();
}
}
Q5. JLbel 컴포넌트는 Key 이벤트를 받을 수 있다. JLabel 컴포넌트를 이용하여 "Love Java"를 출력하고 +키를 치면 폰트 크기를 5픽셀씩 키우고, -키를 치면 폰트 크기를 5픽셀씩 줄이는 스윙 응용프로그램을 작성하라. 5픽셀 이하로 작아지지 않도록 하라.
package problem;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class chapter10 extends JFrame {
JLabel label = new JLabel("Love Java");
public chapter10() {
super.setTitle("Left 키로 문자열 교체");
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = super.getContentPane();
c.setLayout(new FlowLayout());
label.setFont(new Font("Arial", Font.PLAIN, 10));
label.addKeyListener(new key());
c.add(label);
super.setSize(350, 200);
super.setVisible(true);
label.setFocusable(true);
label.requestFocus();
}
class key extends KeyAdapter{
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();
Font f = label.getFont();
int size = f.getSize();
if(keycode == 61) {
label.setFont(new Font("Arial", Font.PLAIN, size + 5));
}
else if(keycode == 45) {
label.setFont(new Font("Arial", Font.PLAIN, size - 5));
}
}
}
public static void main(String args[]) {
chapter10 hello = new chapter10();
}
}
Q6. 클릭 연습용 스윙 응용프로그램을 작성하라. JLabel을 이용하여 문자열이 "C"인 레이블을 하나 만들고 초기 위치를 (100, 100)으로 하라. 문자열을 클릭할 때마다 레이블은 프레임 내의 랜덤한 위치로 움직인다.
package problem;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.math.*;
public class chapter10 extends JFrame {
JLabel label = new JLabel("C");
public chapter10() {
super.setTitle("Left 키로 문자열 교체");
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = super.getContentPane();
c.setLayout(new FlowLayout());
label.setSize(30, 30); label.setLocation(100, 100);
c.add(label);
label.addMouseListener(new mouse());
super.setSize(350, 200);
super.setVisible(true);
}
class mouse extends MouseAdapter{
public void mousePressed(MouseEvent e) {
int x = (int)(Math.random()*340);
int y = (int)(Math.random()*190);
label.setLocation(x, y);
}
}
public static void main(String args[]) {
chapter10 hello = new chapter10();
}
}
Q7. JLbel을 활용하여 "Love Java"를 출력하고, "Love Java" 글자 위에 마우스를 올려 마우스 휠을 위로 굴리면 글자가 작아지고, 아래로 굴리면 글자가 커지도록 프로그램을 작성하라. 폰트 크기는 한 번에 5픽셀씩 작아지거나 커지도록 하고, 5픽셀 이하로 작아지지 않도록 하라.
package problem;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.math.*;
public class chapter10 extends JFrame {
JLabel label = new JLabel("Love Java");
public chapter10() {
super.setTitle("Left 키로 문자열 교체");
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = super.getContentPane();
c.setLayout(new FlowLayout());
label.setFont(new Font("Arial",Font.PLAIN,10));
c.add(label);
label.addMouseWheelListener(new mouse());
super.setSize(350, 200);
super.setVisible(true);
}
class mouse extends MouseAdapter{
public void mouseWheelMoved(MouseWheelEvent e) {
int n = e.getWheelRotation();
Font f = label.getFont();
int size = f.getSize();
if(n < 0) {
label.setFont(new Font("Arial",Font.PLAIN,size + 5));
}
else if(n > 0) {
label.setFont(new Font("Arial",Font.PLAIN,size - 5));
}
}
}
public static void main(String args[]) {
chapter10 hello = new chapter10();
}
}