티스토리 뷰
Event
컴포넌트(버튼 등)에서 사용자에 의해 어떤 상호작용이 일어나는 것
- 버튼 클릭, 마우스 이동, 키보드 입력, 체크박스 선택
이벤트가 발생했을 때 어떤 동작을 수행하기 위해서는 대상 컴포넌트와 이벤트를 처리하는 이벤트 리스너를 서로 연결해야한다. 각 컴포넌트에 따라 서로 다른 리스너가 제공된다(버튼 클릭 이벤트 담당 : ActionListener 사용)
컴포넌트 객체의 addXXX() 메서드를 호출해 리스너 객체를 파라미터로 전달하여 연결한다.
- XXX는 담당 리스너 인터페이스(또는 클래스)이름
- btn.addActionListener(리스너객체);
이벤트 처리 Event Handling
컴포넌트에 특정 이벤트가 발생했을 때 수행할 동작을 지정하여 처리하는 것
리스너 인터페이스(또는 어댑터 클래스) 구현을 통해 내부에 수행할 동작을 명시
XXXListener 인터페이스, XXXAdapter 클래스가 제공
리스너 객체를 직접 구현하거나, 별도의 핸들러 클래스(Handler)클래스를 정의해 리스너 인터페이스(또는 어댑터 클래스)를 상속받아 구현
package event_handling;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JFrame;
public class Ex1 {
public Ex1() {
showFrame();
}
public void showFrame() {
JFrame f = new JFrame("이벤트 처리");
f.setBounds(600, 400, 300, 300); // 프레임 크기
MyWindowListener listener = new MyWindowListener();
f.addWindowListener(listener);
f.setVisible(true);
// 프레임을 하나 더 만드는데 리스너는 똑같은 걸로
JFrame f2 = new JFrame();
f2.setBounds(800, 400, 400, 400);
// 처리할 이벤트가 동일하면 새 객체를 생성하지 않고
// 기존에 생성한 리스너 객체를 그대로 재사용 가능
f2.addWindowListener(listener);
f2.setVisible(true);
}
public static void main(String[] args) {
new Ex1();
}
}
// 윈도우리스너를 상속받는 클래스를 하나 만들어=잘모를때
class MyWindowListener implements WindowListener{
@Override
public void windowOpened(WindowEvent e) {
// 프로그램이 처음 실행될 때 프레임 생성되며 표시되면 호출되는 메서드(1회)
System.out.println("windowOpened");
}
@Override
public void windowClosing(WindowEvent e) {
// 맨 마지막 프로그램이 종료될 때 닫기버튼 클릭하면 호출
System.out.println("windowClosing");
// 프로그램 종료(0: 정상종료, 이외의 값: 비정상 종료)
System.exit(0);
}
@Override
public void windowClosed(WindowEvent e) {
System.out.println("windowClosed");
}
@Override
public void windowIconified(WindowEvent e) {
// 프레임 최소화 버튼 클릭 시 호출
System.out.println("windowIconified");
}
@Override
public void windowDeiconified(WindowEvent e) {
// 프레임 최소화 해제 시 호출
System.out.println("windowDeiconified");
}
@Override
public void windowActivated(WindowEvent e) {
// 프레임이 사용자와 상호작용이 가능한 상태가 될 때 호출(활성화)
System.out.println("windowActivated");
}
@Override
public void windowDeactivated(WindowEvent e) {
// 프레임이 사용자와 상호작용이 불가능한 상태가 될 때 호출(비활성화)
// 다른 프로그램에 가려진 상태가 될 때
System.out.println("windowDeactivated");
}
}
프레임이 생성
package event_handling;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class Ex2 {
public Ex2(){
showFrame();
}
public void showFrame() {
JFrame f = new JFrame("이벤트 처리-2");
f.setBounds(300,400,400,400);
// windowlist -> window adapter - 업캐스팅
MyWindowAdapter listener = new MyWindowAdapter();
f.addWindowListener(listener);
f.setVisible(true);
}
public static void main(String[] args) {
new Ex2();
}
}
// 클래스기 때문에 상속
class MyWindowAdapter extends WindowAdapter {
// 상속받은 슈퍼클래스의 메서드 중 필요한 메서드만 선택적 오버라이딩
// => windowClosing() 메서드 오버라이딩
@Override
public void windowClosing(WindowEvent e) {
System.out.println("windowClosing");
super.windowClosing(e);
}
}
프레임이 생기고 닫을 때 windowCLosing이 출력된다.
< 이벤트 처리 5단계 >
1. 리스너 인터페이스를 구현하는 구현체 클래스(핸들러)를 정의
이벤트 발생 시 수행할 동작을 구현체 내부의 메서드에 기술하고
리스너 연결 시 구현체 객체 생성하여 addXXXListener() 메서드 파라미터로 전달
2. 리스너 인터페이스 대신 어댑터 클래스 사용
- 추상메서드가 2개 이상인 인터페이스를 구현하면 불필요한 메서드까지 구현해야하므로 코드가 길어짐
- 리스너 인터페이스를 미리 구현해놓은 어댐터 클래스를 상속받는 핸들러에서
원하는 메서드만 선택하여 오버라이딩이 가능하므로 코드가 간결
- XXXListener 인터페이스에 대응하는 XXXAdapter 클래스가 제공
추상메서드가 하나뿐인 인터페이스는 어댑터 클래스가 제공되지 않음
ActionListener 인터페이스의 추상메서드가 1개이므로 ActionAdapter 제공되지 않음
3. 내부클래스(Inner Class)형태로 정의
리스너 인터페이스 또는 어댑터 클래스 구현 시 내부클래스 형태로 구현하여 사용
=> 이벤트 리스너 구현체(핸들러 클래스)는 보통 GUI 클래스에서만 사용됨 ( = 전용클래스)
따라서, 별도의 클래스로 외부에 정의하기보단 내부클래스 형태로 정의해서 사용
GUI 클래스 내부에 핸들러 클래스를 정의하면 내부클래스가 됨
=> 외부에 정의하는 방법과 클래스 모양은 동일하나 클래스 정의 위치만 달라짐
4. 익명 내부클래스(Anonymous Inner Class) 형태로 정의
- 리스너 인터페이스 또는 어댑터 클래스를 구현하는 핸들러를 별도로 정의하지 않고 (그때그때 정하는 게 좋다) 해당 리스너나 어댑터의 이름을 그대로 사용해 변수 선언 및 인스턴스 생성과 추상메서드 구현까지 한꺼번에 수행하는 방법
- 개발자가 별도의 핸들러 이름을 부여하지 않으므로 익명 클래스
- 3단계 위치와 동일하며 클래스 정의 방법만 다르다.
public class Ex1 {
public Ex1() {
showFrame();
}
public void showFrame() {
JFrame f = new JFrame("이벤트 처리 - 4");
f.setBounds(600, 400, 600, 500);
// 익명 내부 클래스타입으로 바로 설계해줍시다
WindowAdapter listener = new WindowAdapter() {
// 따로 클래스를 만들지 않고 바로 오버라이딩(괜히 파일만 늘어나니까)
@Override
public void windowClosing(WindowEvent e) {
System.out.println("로컬 windowClosing");
System.exit(0);
}
};
f.addWindowListener(listener);
f.setVisible(true);
}
public static void main(String[] args) {
new Ex1();
}
}
어? 이거 람다식 되는 거 아닌가?
// 람다식 X Adapter는 추상메서드도 인터페이스도 아니다
// WindowAdapter listener2 = e -> {
// System.out.println("로컬 windowClosing");
// System.exit(0);
// };
안 된다!
익명 내부클래스 형태로 정의
WindowAdapter 또는 WindowListener 이름을 그대로 사용하여 멤버 내부클래스 형태로 정의
인스턴스 멤버와 동일한 위치에 정의하므로 인스턴스 내부 클래스라고도 함
5. 익명 내부 클래스의 임시 객체 형태로 사용
- 기본적 개념은 익명 내부클래스와 동일하나, 이벤트 처리 대상이 하나뿐일 경우 별도의 변수가 필요없으므로 변수 선언부를 제외하고 리스너 연결을 위한 addXXXListener() 메서드 파라미터로 익명 내부클래스를 구현하는 코드를 바로 기술하여 객체를 전달
- 별도의 변수가 없으므로(바로 연결할 거다) 두 개 이상의 컴포넌트에 리스너 연결이 불가능(즉 1회용 리스너 객체가 됨)
- 리스너 인스턴스 생성 및 추상클래스 구현과 함께 연결 작업까지 한꺼번에 수행
- 동일한 이벤트를 다른 대상에 적용해야할 경우 중복되는 코드가 발생 => 4단계 방법이 더 효율적이다!
public class Test2 {
public Test2() {
showFrame();
}
public void showFrame() {
// 이벤트 처리5. 익명 내부클래스의 임시객체 형태로 이벤트 처리
// JButton 컴포넌트에 대한 이벤트 처리 담당 리스너 : ActionListener 인터페이스
JFrame f = new JFrame();
f.setBounds(400,300,600,500);
JButton btn = new JButton("버튼");
f.add(btn);
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("클릭!");
System.exit(0);
}
});
f.setVisible(true);
}
public static void main(String[] args) {
new Test2();
}
}
람다식으로!
btn.addActionListener(e -> System.out.println("람다 버튼 클릭"));
< 위치에 따른 차이점 >
1) 멤버 레벨에 정의하는 멤버 내부클래스(인스턴스 내부 클래스)
=> 멤버 변수의 성격과 동일한 클래스가 됨(= 접근 범위가 멤버변수와 동일해짐)
2) 메서드 내부에 정의하는 로컬내부 클래스(메서드 종료 시 사라짐)
package event_handling;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class Ex3 {
public Ex3() {
showFrame();
}
public void showFrame() {
JFrame f = new JFrame("이벤트처리-3");
f.setBounds(600, 400, 300, 300);
// 로컬 내부 클래스 형태로 정의
class MyLocalWindowAdapter extends WindowAdapter {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("windowClosing");
System.exit(0);
}
}
// 1) 멤버 내부 클래스 객체 생성 후 연결
MymemberWindowAdapter listener = new MymemberWindowAdapter();
f.addWindowListener(listener);
// 2) 로컬 내부 클래스 객체 생성 후 연결
// 다른 데서 만들 시 똑같은 거 또만들어야하니까 멤버로 만드는 게 나음
// MyLocalWindowAdapter listener = new MyLocalWindowAdapter();
// f.addWindowListener(listener);
//
f.setVisible(true);
}
public void showFrame2() {
MymemberWindowAdapter listener; // 접근 가능
// 메서드가 달라지면 로컬 내부클래스는 접근이 불가능하므로
// MyLocalWindowAdapter listener;
}
public static void main(String[] args) {
new Ex3();
}
// 1) 멤버 내부클래스 형태로 정의
class MymemberWindowAdapter extends WindowAdapter {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("windowClosing");
System.exit(0);
}
}
}
'배운 것 기록 > java' 카테고리의 다른 글
JTextField, JPanel (0) | 2022.07.27 |
---|---|
레이아웃(layout) (0) | 2022.07.26 |
Swing (0) | 2022.07.18 |
DecimalFormat, MessageFormat 클래스 (0) | 2022.07.16 |
자바 I/O - Input / Output (0) | 2022.07.13 |