▶ 이벤트 처리


- 키보드나 마우스를 클릭하거나 마우스를 움직이는 등의 컴퓨터 내의 동작들을 이벤트(Event)가 발생했다고 하며, 해당 동작들을 어떻게 처리할지 결정하는 것을 이벤트 처리라고 한다. 해당 이벤트는 이벤트 리스너(Event Listener) 객체를 생성하여 처리한다.


- 이벤트-구동 프로그래밍(Event-Driven Programming) : 대부분의 프로그램은 알고리즘에 의해 문장들이 차례대로 실행된다. 그러나 이벤트-구동 프로그래밍의 경우 순서에 상관없이 이벤트가 발생함에 따라 프로그램을 실행한다.




 이벤트 객체


- 이벤트가 발생했을 때, 해당 이벤트를 처리하기 위해 이벤트를 보관 또는 저장하는 객체라고 생각하면 될 것이다. 


모든 이벤트 객체는 EventObject 클래스를 상속받는다.


- EventObject 클래스는 getSource() 메소드를 가지고 있으며이 메소드는 해당 이벤트를 일어나게 만든 이벤트 소스를 반환한다. 이벤트 소스란 어떤 원인(버튼 클릭, 키보드 입력 등)에 의해 이벤트가 발생했는지를 알려준다. 리턴 타입이 Object 이므로 필요한 타입으로 타입 변환하여 사용해야 한다.




 이벤트 리스너(Event Listener)


이벤트가 발생하면 이벤트에 대한 다양한 정보를 가진 이벤트 객체가 생성된다이 때발생된 이벤트 객체에 반응하여 이벤트를 처리하는 객체를 의미한다. 리스너는 인터페이스이므로 리스너 인터페이스를 구현하는 클래스와 이벤트 객체를 연결하면 된다.


- 이벤트 리스너 등록하기


public class Test extends JFrame {


//FIELDS

JButton button = new JButton("버튼“);


//CONSTRUCTOR

public Test() {

// 리스너를 등록할 때 매개변수로 이벤트 리스너 인스턴스(클래스)를 넘긴다.

button.addActionListener(new EventListenerA());

...

}

} 


이벤트 소스에 이벤트 리스너가 등록되어 있다면이벤트 발생 시 이벤트 리스너 내부의 이벤트 처리 메소드가 자동 호출된다등록된 리스너가 없으면 아무 일도 발생하지 않는다(컴포넌트의 addXXXXListener() 메소드 호출) 


- 이벤트 리스너를 생성하는 방법은 다양하다.


1. 리스너를 독립적인 클래스로 생성

=> 리스너 클래스가 이벤트가 발생한 클래스 내부의 멤버로 접근하는 것이 어렵다.


//이벤트 리스너를 구현하는 독립적인 클래스

class ListenerExample implements ActionListener {


@Override

public void actionPerformed(ActionEvent e) {

...//이벤트 처리 코드 작성

}


}

 

class FrameExample extends JFrame {


//FIELDS

private JButton button = new JButton("button");


//CONSTRUCTOR

public FrameExample() {

...

button.addActionListener(new ListenerExample()); //리스너는 필드에 선언해도 상관없다.

...

}


}

 

public class Main() {

public static void main(String[] args) {

FrameExample fe = new FrameExample();

}

}



2. 내부 클래스로서 리스너 클래스를 생성

=> 내부 클래스는 외부 클래스의 멤버 변수들을 자유롭게 사용할 수 있다.


//이벤트 리스너를 구현하는 중첩 클래스 - 인스턴스 멤버 클래스

class FrameExample extends JFrame {


//FIELDS

private JButton button = new JButton("button");


//CONSTRUCTOR

public FrameExample() {

...

button.addActionListener(new ListenerExample()); //리스너는 필드에 선언해도 상관없다.

...

}

 

private class ListenerExample implements ActionListener { //중첩 클래스

@Override

public void actionPerfomed(ActionListener e) {

...//이벤트 처리 코드 작성

}

}

}

 

public class Main() {

public static void main(String[] args) {

FrameExample fe = new FrameExample();

}

}



3. 해당 클래스가 이벤트를 바로 처리

=> actionPerformed(ActionEvent e) 메소드를 클래스 내부에 생성한다.


//해당 클래스가 이벤트 리스너를 구현한다.

class FrameExample extends JFrame implements ActionListener {


//FIELDS

JButton button = new JButton("button");

 

//CONSTRUCTOR

public FrameExample() {

...

button.addActionListener(this); //자기 자신이 리스너의 구현 클래스이므로 this를 매개변수로 준다.

...

}

 

//METHODS

@Override

public void actionPerformed(ActionListener) {

...//이벤트 처리 코드 작성

}

}


public class Main() {

public static void main(String[] args) {

FrameExample fe = new FrameExample();

}

}


 

4. 이벤트를 한번만 사용할 경우 무명 클래스 사용 (익명 객체)

=> 코드가 정의되자마자 바로 사용됨.


//처리하는 이벤트가 적은 경우에 주로 사용되며, 이벤트를 일으키는 컴포넌트가 많은 경우에는 추천하지 않는다.

class FrameExample extends JFrame implements ActionListener {


//FIELDS

JButton button = new JButton("button");

 

//CONSTRUCTOR

public FrameExample() {

...

button.addActionListener(new ActionListener() {

@Override

public void actionPerformed(ActionEvent e) {

if(e.getSource() == button)) { ... }

}

});

...

}

}


public class Main() {

public static void main(String[] args) {

FrameExample fe = new FrameExample();

}

}



5. 이벤트 핸들러 클래스 사용

=> 하나의 문장으로 간단하게 이벤트 리스너를 생성하므로 코드의 간결화를 유도한다.


//아주 간단한 타입의 이벤트 리스너에 대해서만 유용하다.

class FrameExample extends JFrame implements ActionListener {


//FIELDS

JButton button = new JButton("button");

 

//CONSTRUCTOR

public FrameExample() {

...

button.addActionListener((ActionListener) EventHandler.create(ActionListener.class, JFrame메소드명));

...

}

}


public class Main() {

public static void main(String[] args) {

FrameExample fe = new FrameExample();

}

}




 이벤트 종류


모든 컴포넌트가 제공하는 이벤트

 ComponentEvent 

 컴포넌트 위치 및 크기가 변경되면 발생

 FocusEvent 

 키보드 입력을 받거나 입력을 받지 않는 경우에 발생

 ContainerEvent 

 컨테이너에 컴포넌트가 추가 혹은 삭제될 경우 발생

 KeyEvent 

 키를 누르면키보드 포커스를 가지고 있는 객체에서 발생

 MouseEvent 

 마우스가 Clicked, Pressed, Released, Entered, Exited의 상황에서 발생

 MouseMotionEvent 

 마우스가 움직였을 때 발생

 MouseWheelEvent 

 컴포넌트 위에서 마우스 휠을 움직이면 발생

 WindowEvent 

 윈도우가 열리거나 닫히거나 아이콘화 되는 등과 같이 어떤 변화가 있을 때 발생 


 

 일부 컴포넌트만 제공하는 이벤트

 ActionEvent 

 사용자가 어떤 동작을 할 경우 발생

 해당 컴포넌트 : JButton, JCheckBox, JComboBox, JFileChooser, JMenuItem, JRadioButton,  JTextField

 CaretEvent 

 텍스트 삽입점이 이동하거나 텍스트 선택이 변경될 경우 발생

 해당 컴포넌트 JTextArea, JTextField

 ChangeEvent 

 객체의 상태가 변경되었을 경우 발생

 해당 컴포넌트 JButton, JCheckBox, JColorChooser, JMenuItem, JProgressBar, JRadioButton, JSlider,

 JSpinner

 DocumentEvent 

 문서의 상태가 변경되는 경우 발생

 해당 컴포넌트 JTextArea, JTextField

 ItemEvent 

 선택 가능한 컴포넌트에서 사용자가 선택하는 경우 발생

 해당 컴포넌트 JButton, JCheckBox, JComboBox, JMenuItem, JRadioButton

 ListSelectionEvent 

 테이블이나 리스트에서 선택이 변경되면 발생

 해당 컴포넌트 JList, JTable

 TreeSelectionEvent

 트리 구조의 컴포넌트에서 선택이 변경되면 발생
 해당 컴포넌트 : JTree




 리스너 인터페이스 요약


리스너 인터페이스

어댑터 클래스

메소드

ActionListener

X

actionPerformed()

AdjustmentListener

X

adjustmentValueChanged()

ComponentListener

ComponentAdapter

componentHidden(), componentMoved(), componentResized(),

componentShown()

ContainerListener

ContainerAdapter

componentAdded(), componentRemoved()

FocusListener

FocusAdapter

focusGained(), focusLost()

ItemListener

X

itemStateChanged()

KeyListener

KeyAdapter

KeyPressed(), KeyTyped(), KeyReleased()

MouseListener

MouseAdapter

mouseClicked(), mouseEntered(), mouseExited(), mousePressed(), 

mouseReleased()

MouseMotionListener

MouseMotionAdapter

mouseDragged(), mouseMoved()

TextListener

X

textValueChanged()

WindowListener

WindowAdapter

windowActivated(), windowClosed(), windowClosing(),

windowDeactivated(), windowDeiconified(), windowIconified()

windowOpened()




▶ 어댑터 클래스

 

이벤트를 처리하기 위해서는 리스너 인터페이스에서 정의되어 있는 모든 메소드를 구현해야 한다따라서 원하는 메소드가 하나뿐인 경우에도 인터페이스의 모든 메소드를 구현해야 하는 불편함이 있다.

 

어댑터 클래스(Adapter Class)는 각 리스너(Listener)에 대응되는 클래스로원하는 메소드만을 구현하는 것이 가능하다.

 

리스너는 인터페이스고 어댑터는 클래스의 형태로 제공되기 때문에 리스너를 사용할 때는 implements 키워드를어댑터를 사용할 때는 extends 키워드를 사용해야 한다.

 

자바에서는 다중 상속을 허용하지 않기 때문에 두 개의 클래스를 동시에 상속받을 수 없다따라서 어댑터 클래스를 내부 클래스(중첩 클래스)로 정의하여 사용한다.

 

리스너의 메소드가 하나인 의미적 메소드의 경우 대응하는 어댑터 클래스가 없다.


public class MyFrame extends JFrame {



MyFrame() {

...

addKeyListener(new MyKeyAdapter());

...

}

 

class MyKeyAdapter extends KeyAdapter {

@Override

public void keyTyped(KeyEvent e) { //하나의 메소드만 처리 가능

...//이벤트 처리 코드 작성

}

}

}






+ Recent posts