구현방법
1. 이벤트를 발생시키는 주체 찾기
2. 이벤트 주체에 해당하는 이벤트 종류 선택하기
3. 이벤트를 등록한다.
이벤트 주체.addㅁㅁㅁListener( .... );
4. 해당 이벤트를 처리하는 핸들러를 구현한다.
이벤트 구현은 생성자 구현부 마지막부에서 주로 위치시킨다.
Listener는 인터페이스이고, Adapter는 클래스 형태지만, 실제로 Adapter는 내부적으로 인터페이스를 미리 implements 해준 것과 같다. 또 메서드가 하나밖에 없는 인터페이스들은 Adapter가 존재하지 않는다. 어차피 재정의해야하기 때문에..
간단한 Action Event 예제
import java.awt.FlowLayout; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JOptionPane; public class ActionExam extends Frame implements ActionListener { // 버튼 생성 JButton btn1 = new JButton("클릭1"); JButton btn2 = new JButton("클릭2"); //생성자 설정 public ActionExam() { // 레이아웃 설정 this.setLayout(new FlowLayout()); // 버튼 추가 this.add(btn1); this.add(btn2); // 크기 및 보기 설정 this.setSize(300, 200); this.setVisible(true); // 이벤트 등록하기 btn1.addActionListener(this); btn2.addActionListener(this); } public static void main(String[] args) { // 실행 new ActionExam(); } @Override public void actionPerformed(ActionEvent e) { //액션 리스너 재정의 if (e.getSource().equals(btn1)) { JOptionPane.showMessageDialog(this, "1번 버튼 눌렀네"); } else { JOptionPane.showMessageDialog(this, "2번 버튼 눌렀네"); } } }
인터페이스를 implements 하여 클래스 자신이 이벤트를 처리하는 구조이다.
인터페이스의 모든 abstract 메소드를 재정의하여 처리해준 것을 확인할 수 있다.
이벤트를 내 자신이 처리하는 이 방식이 가장 기본이 되는데,
★ 이벤트 처리를 내가 하지 않고 다른 클래스에 넘겨서 처리할 수 있다 ★
this.addWindowListener( /* this */ new WinExam() );
창닫기 버튼을 눌렀을 때 이벤트 핸들러를 예제에서는 this 로 나 자신을 가리키게 했는데, 위와 같이 외부 클래스에서 처리하도록 하는 것과 같이 적용할 수 있다. 처리하는 외부 클래스는 아래와 같이 어댑터를 상속받아 해당 이벤트를 처리하도록 구현된다.
import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; public class WinExam extends WindowAdapter { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }
이번에는 이벤트 처리를 하는 몇 가지 방법을 살펴보자.
하나는 implements를 사용해 인터페이스를 가져와 처리하는 방법과, 외부 클래스로 넘겨서 처리하는 방법 두가지이다.
전자는 모든 메소드를 재정의해주어야하기때문에 소스가 길어지지만 한 클래스 내에서 처리하기 때문에 객체 접근 등에서 더 편리하고 외부 클래스로 넘겨서 처리하는 방법은 소스는 깔끔해지지만, 객체에 접근하기 위해서는 객체 주소를 넘겨주는 방법까지 고민해야하기 때문에 구현에 난이도가 높아진다.
외부 클래스로 넘겨서 처리하는 방법은,
① Inner Class로 받아서 처리하는 방법과
② 익명 클래스로 처리하는 방법으로 더 발전시켜 적용할 수 있다.
하나씩 살펴보자!!
인터페이스를 가져와 이벤트 처리하는 방법
가장 기초적인 방법이다.
이벤트를 처리하는 인터페이스를 implements로 가져와서 내 자신 클래스에서 처리하는 방법이다. 메소드들을 재정의하고 해당되는 메소드들에 내가 원하는 것을 수행하도록 한다. abstract 메소드들을 모두 재정의해야하기 때문에 이 부분으로 인해 소스가 복잡해진다.
import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextField; public class MouseExam extends JFrame implements MouseMotionListener, MouseListener { JLabel label = new JLabel("마우스 드래그 해보자"); JTextField jtextfiled = new JTextField(); public MouseExam() { this.add(label, "North"); this.add(jtextfiled, "South"); this.setSize(300, 400); this.setVisible(true); //X버튼 눌렀을 때 종료 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //이벤트 리스너 추가 this.addMouseMotionListener(this); this.addMouseListener(this); } public static void main(String[] args) { new MouseOuterExam(); } @Override public void mouseDragged(MouseEvent e) { //드래그시 좌표 출력 jtextfiled.setText("(" + e.getX() + ", " + e.getY() + ")"); } @Override public void mouseMoved(MouseEvent e) {} @Override public void mouseClicked(MouseEvent e) {} @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) { //마우스 위에 있으면 안쪽 메시지 출력 jtextfiled.setText("마우스 안쪽?"); } @Override public void mouseExited(MouseEvent e) { //마우스 위에 있으면 바깥쪽 메시지 출력 jtextfiled.setText("마우스 바깥쪽?"); } }
외부 클래스로 넘겨서 이벤트 처리하는 방법
외부 클래스로 처리하는 방법은 불필요한 오버라이딩이 없기 때문에 좀더 단순해지지만, 구조적으로는 좀더 복잡한 형태이다. 이벤트 리스너에서 이벤트를 처리할 부분을 앞의 인터페이스를 받아 처리하는 방식에서는 나 자신이 처리하므로 this로 잡아주었지만, 여기서는 그 부분을 외부의 클래스로 잡아준다. 외부의 클래스이므로 new 키워드를 사용해 해당 클래스를 호출한다.
import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextField; //MouseMotionAdapter를 상속받아 mouseDragged 메소드를 재정의하는 클래스이다. //마우스를 드래그할 때의 좌표를 텍스트필드에 쓰기위해서 부모의 주소를 //생성자를 통해 받아올 수 있도록했다. class MouseMotionClass extends MouseMotionAdapter { MouseExam superClass; // 부모객체 담을 곳 @Override public void mouseDragged(MouseEvent e) { superClass.jtextfiled.setText("(" + e.getX() + ", " + e.getY() + ")"); } MouseMotionClass(MouseExam superClass) { // 인자로 받은 부모 객체를 전역변수에 저장 this.superClass = superClass; } } // MouseAdapter를 상속받아 mouseEntered,mouseExited 메소드를 재정의하는 클래스이다. // 마우스 이동 상황을 텍스트필드에 쓰기위해서 부모의 주소를 생성자를 통해 받아올 수 // 있도록했다. class MouseClass extends MouseAdapter { MouseExam superClass; // 부모객체 담을 곳 @Override public void mouseEntered(MouseEvent e) { superClass.jtextfiled.setText("마우스 안쪽?"); } @Override public void mouseExited(MouseEvent e) { superClass.jtextfiled.setText("마우스 바깥쪽?"); } MouseClass(MouseExam superClass) { // 인자로 받은 부모 객체를 전역변수에 저장 this.superClass = superClass; } } public class MouseExam extends JFrame { JLabel label = new JLabel("마우스 드래그 해보자"); JTextField jtextfiled = new JTextField(); public MouseExam() { add(label, "North"); add(jtextfiled, "South"); setSize(300, 400); setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //이벤트 리스너를 등록하며 JFrame 객체의 주소를 함께 보낸다. this.addMouseMotionListener(new MouseMotionClass(this)); this.addMouseListener(new MouseClass(this)); } public static void main(String[] args) { new MouseExam(); } }
Inner Class로 이벤트 처리하는 방법
이번에는 Inner Class로 처리하는 방법을 살펴보자.
위의 외부 Class와 비슷하지만, 객체를 넘겨주는 부분을 쉽게 처리할 수 있기 때문에 간편해진다.
Inner Class는 단순히 외부의 클래스들을 클래스 내부로 가져다가 붙여넣는 것 뿐이다.
또 객체 등을 넘겨주었던 부분이 더이상 필요해지지 않고, 부모 클래스에 선언되어 있던 전역 객체를 그대로 사용할 수 있다.
import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextField; public class MouseExam extends JFrame { JLabel label = new JLabel("마우스 드래그 해보자"); JTextField jtextfiled = new JTextField(); public MouseExam() { add(label, "North"); add(jtextfiled, "South"); setSize(300, 400); setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 이벤트 리스너를 등록하며 JFrame 객체의 주소를 함께 보낸다. this.addMouseMotionListener(new MouseMotionClass()); this.addMouseListener(new MouseClass()); } public static void main(String[] args) { // 실행 new InnerExam(); } // Inner Class class MouseMotionClass extends MouseMotionAdapter { @Override public void mouseDragged(MouseEvent e) { jtextfiled.setText("(" + e.getX() + ", " + e.getY() + ")"); } } // Inner Class class MouseClass extends MouseAdapter { @Override public void mouseEntered(MouseEvent e) { jtextfiled.setText("마우스 안쪽?"); } @Override public void mouseExited(MouseEvent e) { //앞에 MouseExam.this 가 생략된 것으로 볼 수 있다. (MouseExam.this).jtextfiled.setText("마우스 바깥쪽?"); } } }
익명 클래스로 처리하는 방법
가장 시인성이 좋은 방식이지만, 또 상대적으로 가장 객체의 재사용성이 떨어지는 방식이다.
위의 내부 클래스로 호출하여 처리하는 부분에 아예 클래스 내용 자체를 넣는 것처럼 보인다(ㅋㅋ)
익명 클래스이므로
new MouseMotionClass() 이런 식으로 핸들러를 호출했던 부분은 실상은,
class MouseMotionClass extends MouseMotionAdapter 를 호출했던 것이다.
익명이라는 이름답게 앞의 부분은 모두 생략하고
new MouseMotionAdapter() { ... }
형태로 상속받는 클래스만 남기고 원래 이름은 모두 삭제되어 대체한다. 그리고 바로 { 를 열어 클래스 안의 내용을 작성해준다.
import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextField; public class InnerExam extends JFrame { JLabel label = new JLabel("마우스 드래그 해보자"); JTextField jtextfiled = new JTextField(); public InnerExam() { add(label, "North"); add(jtextfiled, "South"); setSize(300, 400); setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 이벤트 리스너를 등록하며 JFrame 객체의 주소를 함께 보낸다. this.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { jtextfiled.setText("(" + e.getX() + ", " + e.getY() + ")"); } }); this.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { jtextfiled.setText("마우스 안쪽?"); } @Override public void mouseExited(MouseEvent e) { //앞에 InnerExam.this 가 생략된 것으로 볼 수 있다. (InnerExam.this).jtextfiled.setText("마우스 바깥쪽?"); } }); } public static void main(String[] args) { // 실행 new InnerExam(); } }