JAVA공부 [7. JAVA SWING(1)]
1. 🔥 Swing
swing은 자바의 GUI(graphicla User Interface) 즉 ,API이다..!
지금 까지 앞서 다룬 것들은 console프로그램이였다면 우리에게 좀더 친숙한 마우스나 키보드로 입력 가능하게 해주는 라이브러리들을 GUI라고 통칭한다.
c++은 MFC, QT을 예로 들 수 있다.
- 자바 GUI(swing)의 특징
- 강력하고 쉬운 프로그래밍 가능하다.
- 초기에 AWT라는 라이브러리가 존재했지만 시간에 따라 업그레이드 되어 Swing등장
- 운영체제에 종속적이지 않는다.
- 모든 클래스이름앞에는 J가 붙는다.
- 하나의 요소를 다룰 수 있는 Jcomponent클래스와 그러한 요소들을 담을 수 있는 Container클래스가 존재한다.
awt는 java.awt패키지, swing은 javax.swing패키지
최근에는 java.FX패키지가 발전되고 있다!
1.1. 컨테이너와 컴포넌트
- 컨테이너
- 다른 GUI컴포넌트를 포함할 수 있는 컴포넌트
- java.awt.container상속
- 다른 컨테이너에 포함될 수 있음
- 최상위 컨테이너
- 다른 컨테이너에 속하지 않고 독립적으로 출력가능한 컨테이너
- 컴포넌트
- 컨테이너에 포함되어야지만 출력가능한 순수 컴포넌트
- 모든 컴포넌트는 java.awt.Component를 상속받음(javax.swing.JComponent)
정리하자면 한 건물이 있을 때 그 건물안에는 여러개의 방이 존재하고 각각의 방에는 각각 다른 가구들이 존재한다.(객체지향 개념)
이때, 건물 자체를 최상위 컨테이너라고 생각하고, 방들은 각각의 가구를 담든 컨테이너, 가구들은 각자의 역할을 하는 컴포넌트의 개념이다.
1.2. 실습
import javax.swing.JFrame; //Swing패키지 JFrame클래스 import
class MyFrame extends JFrame{ // JFrame상속받아 해당 기능 전부 사용가능
}
public class App {
public static void main(String[] args) throws Exception {
MyFrame f = new MyFrame(); //객체 생성
f.setTitle("hello"); // 타이틀 이름
f.setSize(300,300); // 크기
setDefaultCloseOperation(EXIT_ON_CLOSE); // 프로그램 종료 동기화
f.setVisible(true); // 활성화
}
}
- 다음과 같은 GUI가 출력 된다.
이 처럼 기본적인 설정 즉, 크기, 이름, 상태등 과 같은 항목들은 main에서 매번 설정할 필요가 있을까?
- 설정할 필요 없이 해당 클래스의 생성자를 호출하면 된다..!
- 이미 JFrame을 상속 받은 순간 부터 객체를 생성할 때 우리눈에는 보이지 않는(캠슐화)되어 있는 JFrame의 생성자를 호출한다.
import javax.swing.JFrame;
class MyFrame extends JFrame{
MyFrame(){
setTitle("hello");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}//어떤 메서드가 있는지 모를 때..! this키워드를 사용하여 메서드 도움말 열람 가능
}
public class App {
public static void main(String[] args) throws Exception {
MyFrame f = new MyFrame();
}
}
- 위와 동일하게 동작한다..! 일반화 버전
swing 객체지향의 특성!
- 생성자에 매개변수로 이름을 받아 타이틀의 이름 동적할당 가능!
- 객체를 여러개 만들어서 여러개의 Frame호출 가능!
- 멀티 쓰레드의 개념
쓰레드란? 지금까지 다룬 프로그램들은 전부 main에서 시작해서 main이 종료될 때 프로그램이 종료되는 단일 쓰레드 프로그램이였다. 하지만 앞으로 다루게 되는 swing클래스는 멀티쓰레드를 지원하기 때문에 비동기적 프로그램으로 실행이 된다.
- 이 개념은 위에 코드를 보면 분명히 main에서는 객체를 생성하고 더이상 수행할게 없기 때문에 종료가 되어야 한다.
실제로 main은 내부적으로 종료가 되었다. 하지만 Frame객체를 생성하였을때 쓰레드하나가 더 동작하여 비동기적 프로그램이 되기 때문에 실제로는 f객체가 무한루프를 돌며 종료를 대기하는 모습이다.
1.3. 컴포넌트 추가
import javax.swing.JButton;
import javax.swing.JFrame;
class MyFrame extends JFrame{
MyFrame(){
setTitle("hello");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
//JButton b = new JButton("Button1");
//add(b); // 아래랑 같은 문법
add(new JButton("Button!")); //생성자 이름추가를 강제함?
// 현재(this)컨테이너에 버튼 컴포넌트를 추가한다.
setVisible(true);
}
}
public class App {
public static void main(String[] args) throws Exception {
MyFrame f = new MyFrame();
}
}
이렇게 전체적인 화면에 버튼이 등장하게 된다..!
- 그렇다면 layout설정이 가능한가? 여러개의 버튼을 추가하고 싶다면?
import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.FlowLayout; //awt패키지의 FlowLayout가져옴
class MyFrame extends JFrame{
MyFrame(){
setTitle("hello");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
//앞서 말한 기초 설정 생성자
setLayout(new FlowLayout());
add(new JButton("Button1"));
add(new JButton("Button2"));
setVisible(true);
}
}
public class App {
public static void main(String[] args) throws Exception {
MyFrame f = new MyFrame();
}
}
- 이처럼 layout설정이 가능하다!
- 다양한 컴포넌트
- JButton 버튼 클래스!
- JTextField 텍스트 입력 클래스!
- JPasswordField 비밀번호 입력(*표시)클래스
- JLabel 원하는 객체 표시 클래스
해당 컴포넌트들은 전부 javax.swing패키지에서 각각의 클래스를 import해야하지만 javax.swing.*로 전부 가져올 수 있다.
컴포넌트 활용
import javax.swing.*;
import java.awt.FlowLayout;
class MyFrame extends JFrame{
MyFrame(){
setTitle("hello");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(new JLabel("아이디 입력"));
add(new JTextField(20));
add(new JLabel("비밀번호 입력"));
add(new JPasswordField(20));
add(new JButton("로그인"));
add(new JButton("취소"));
setVisible(true);
}
}
public class App {
public static void main(String[] args) throws Exception {
MyFrame f = new MyFrame();
}
}
이렇게 간단하게 GUI를 만들 수 있다.
1.4. 추가 개념
현재는 클래스가 두개가 독립적으로 존재하는데 이는 보기에 좋지 않으며 App클래스 자체가 JFrame을 상속받는다면 코드가 좀더 간결해지고 쉽게 볼 수 있지 않을끼?
- 그렇다면 main매서드가 존재하기 때문에 자기 자신을 객체화 하는건데 가능?
main은 static이기 때문에 독립적으로 존재하기 때문에 전혀 영향이 없다.
따라서 위에 코드를 리빌딩하먼 아래와 같아진다.
import javax.swing.*;
import java.awt.FlowLayout;
public class App extends JFrame {
App(){
setTitle("hello");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(new JLabel("아이디 입력"));
add(new JTextField(20));
add(new JLabel("비밀번호 입력"));
add(new JPasswordField(20));
add(new JButton("로그인"));
add(new JButton("취소"));
setVisible(true);
}
public static void main(String[] args){
App f = new App();
}
}
클래스이름과 소스코드이름, JFrame클래스(상속받은)클래스의 이름이 동일하기 때문에 중복성이 줄어들고 훨씬 보기 간편하다.
JFrame이 최상위 컨테이너의 개념 즉, 프로그램 자체이기 때문 따라서 이 자체가 public class가 되는 경우가 많다
- 조금 더 세부적으로 본다면 GUI의 영역에서 위의 툴바 영역과 아래 회색빛 영역으로 나눌 수 있다.
회색빛 역역을 content pane이라고 하는데 이 영역에 대한 객체를 뽑아내서 해당 객체에 직접 add하는 방식으로도 사용가능하다.(조금더 직관적임)
import javax.swing.*;
import java.awt.*;
public class App extends JFrame {
App(){
setTitle("hello");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
//Content pane 영역 직접 지정
Container c = getContentPane();
c.setLayout(new FlowLayout());
c.add(new JLabel("아이디 입력"));
c.add(new JTextField(20));
c.add(new JLabel("비밀번호 입력"));
c.add(new JPasswordField(20));
c.add(new JButton("로그인"));
c.add(new JButton("취소"));
setVisible(true);
}
public static void main(String[] args){
App f = new App();
}
}
- 지금까지는 Frame의 영역에 직접 add하거나 Content Pane에 추가하였는데 분리하거나 좀더 세부적으로 다루고 싶다면?
Panel을 사용하면 된다.
이 개념은 칠판에 종이를 붙일 때 해당 종이에 버튼, text, 색깔등을 미리 정해주고 마지막에 Frame 즉, 최상위 컨테이너에 붙여주는 간단한 개념이다.
import javax.swing.*;
import java.awt.*;
public class App extends JFrame {
App(){
setTitle("hello");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
//panel객체 생성 컨테이너 개념!
JPanel p = new JPanel();
p.setBackground(Color.ORANGE); //배경색 설정
p.setLayout(new FlowLayout());
p.add(new JLabel("아이디 입력"));
p.add(new JTextField(20));
p.add(new JLabel("비밀번호 입력"));
p.add(new JPasswordField(20));
p.add(new JButton("로그인"));
p.add(new JButton("취소"));
this.add(p); //현재 컨테이너 즉, Frame에 추가함.
setVisible(true);
}
public static void main(String[] args){
App f = new App();
}
}
휠씬 깔끔하고 이해가 쉽다..!
앞서 다룬 JPanel을 사용하여 Frame에 붙이는 방식은 객체지향의 개념이다.
- 그렇다면 Panel자체를 클래스로 만들어서 다른 Frame에서도 사용할 수 았게 만든다면?
import java.awt.*;
import javax.swing.*;
class MyPanle extends JPanel{ //Jpanel을 커스터마이징하여 새로운 패널클래스를 생성
MyPanle(){ //생성자를 통해 초기화
setBackground(Color.BLUE);
add(new JButton("Button"));
}
}
public class HelloEventHandling extends JFrame{
public static void main(String []args){
new HelloEventHandling();
}
HelloEventHandling(){
setTitle("hello fkdl");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
MyPanle p = new MyPanle(); // Mypanle객체 생성
add(p);
setVisible(true);
}
}
이렇게 작성된 Panel클래스는 재사용성이 좋아지고 객체지향의 개념을 확립할 수 있다.
앞으로의 코딩방식은 위와 같이 진행된다.
1.5. 정리
setDefaultCloseOperation(EXIT_ON_CLOSE);
- 이코드를 작성하지 않으면 GUI에서의 종료와 프로그램의 종료와 연동되지 않는다. 따라서 어디서든 다시 setVisible호출시 다시 나타나게 됨
- main()이 종료후에도 프로그램이 진행되는 이유
- main의 메인쓰레드는 처음 실행되고 main에서 실행된다.
- 하지만 스윙 응용프로그램이 실행될 때 자동으로 실행되는 이벤트 분배 쓰레드는 무한루프를 돌며 사용자의 입력, 클릭 등을 검사하기 때문에 전체 프로그램은 종료되지 않는다.
- 따라서 종료 조건은 자바의 모든 쓰레드(일꾼)들이 모두 없어져야지 종료된다.
- 컨테이너의 배치 개념
- 컨테이너는 하나의 배치관리자가 존재하는데 이 관리자가 add되는 컴포넌트들의 위치와 크기를 결정하고 적절하게 배치하게 된다.
- 따라서 컨테이너의 크기가 변경되면 관리자에 의해 내부 컴포넌트들의 위치와 크기가 재할당된다.
- 대표 유형 4가지 (FlowLayout, BorderLayout, GridLayout, CardLayout)
- JFrame은 기본적으로 BorderLayout, JPanel은 FlowLayout
2. 🔥 Event Handling
지금까지 해온 순차적 프로그래밍기법과 다르게 이벤트 핸들러를 사용한 프로그래밍 기법은 사용자가 어떠한 약속된 행동을 하기 전까지 대기하다 이벤트가 발생되면 해당 이벤트를 실행하고 다시 대기하는 형식으로 작동하는 프로그래밍 기법이다.
event based(driven) programming
- 미리 정의된 이벤트(마우스 움직임, 키보드 누름 등)
- 위에서 정의된 이벤트가 발생에 의해 프로그램의 흐름이 결정된다.
- 이벤트가 발생하면 이벤트를 처리하는 루틴(이벤트 리스너)실행
- 반대의 개념 배치프로그래밍이 프로그래밍은 언제 어떤 함수가 실행되는지 결정되어 있음
- 이에 반해 event driven programming은 개발자가 미리 정의해둔 이벤트들이 있지만 프로그램의 흐름은 사용자가 결정하게 된다.
- 이벤트 처리 순서
- 이벤트 발생(사용자의 입력)
- 이벤트 객체 생성
- 현재 발생한 이벤트의 정보를 가진 객체
- 이벤트 리스너 찾기
- 이벤트 리스너 호출
- 이벤트 객체가 리스너에게 전달
- 이벤트 리스너 실행
정리하자면 사용자가 어떠한 버튼을 클릭했을 때 바로 클릭이벤트가 실행되고 해당 이벤트 리스너를 찾게 된다 이때 개발자가 구현해 놓은 이벤트리스너가 있다면 해당 리스너를 호출하게 된다.
용어 정리!
- 이벤트 소스
- 이벤트를 발생시킨 주체 즉, GUI컴포넌트(swing의 경우)
- 이벤트 객체
- 만약 사용자가 버튼을 눌렀다면 해당 이벤트에 대한 정보의 덩어리가 만들어지고 이를 이벤트 객체라 한다.
- 이벤트 리스너
- 위에서 말한 이벤트 객체를 받는 것 즉, 개발자가 등록해야 함
- 이벤트 분배 쓰레드
- 이벤트 객체를 이벤트 리스너로 넘겨주는 역할
- 우리가 생성한 Frame의 쓰레드를 예로 들 수 있다.
그렇다면 개발자는 어떻게 이벤트 리스너를 작성할까?
- 사용자가 버튼을 누른다!
- ActionEvent발생!
- 해당 버튼에 연결되어 있는 ActionEvent를 처리할 수 있는 리스너(ActionListener)를 호출한다.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*; //이벤트 패키지 import
class MyButtonActionListener implements ActionListener{ //ActionListener상속받음 추상메서드 구현 강제
@Override
public void actionPerformed(ActionEvent e) { //추상메서드 구현, 이벤트 객체를 매개변수로 받음
System.out.println("button on!");
JButton b = (JButton)e.getSource(); // 이벤트 객체의 정보를 불러오는 메서드 -> 반환값이 object여서 강제 다운캐스팅
b.setText("버튼 눌림!"); //해당 객체를 참조하여 정보 변경
}
//callback method 라고 부르기도 함
}
class MyPanle extends JPanel{
MyPanle(){
setBackground(Color.BLUE);
JButton but = new JButton("Button");
but.addActionListener(new MyButtonActionListener());//implements ActionListener, 어떠한 객체도 상관없지만 ActionListener를 상속받은 객체만
add(but);
}
}
public class HelloEventHandling extends JFrame{
public static void main(String []args){
new HelloEventHandling();
}
HelloEventHandling(){
setTitle("hello fkdl");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
MyPanle p = new MyPanle();
add(p);
setVisible(true);
}
}
- 위 코드에선 버튼의 addActionListener 즉, 눌리는 이벤트가 발생할 때의 이벤트 리스너를 등록하는 메서드를 통해 다른 메서드를 동적으로 호출할 수 있게 한다.
이때, addActionListener메서드의 매개변수는 객체이며 어떠한 객체도 받을 수 있지만 ActionListener인터페이스를 상속받은 객체인 경우에 가능하다..!
중요
-
ActionListener인터페이스는 추상메서드 actionPerformed를 포함하고 있으며 해당 메서드내에 코드를 작성한 객체는 해당 객체를 리스너로 등록하였을 경우 버튼클릭 이벤트 발생 시 해당 버튼에 등록된 리스너 즉, 해당 객체의 actionPerformed메서드에 등록된 코드가 실행된다.
- 그렇다면 구현을 강제하게 되는
public void actionPerformed(ActionEvent e)
메서드는 개발자가 등록해서 사용하는 이벤트 리스너로 사용돠는데 이때 매개변수로 받는 ActionEvent e는 이벤트 객체로 해당 버튼이 눌린 정보를 담고 있다.- 이벤트객체는 다양한 메서드가 사용가능한데 가장 많이 사용되는 메서드가 바로 getSource()이다..
- 이 메서드의 반환값은 항상 Object타입이기 때문에 해당 객체의 직접적인 참조가 필요한경우 다운캐스팅이 필요하다.
- 위의 코드에선 현재 자신의 객체 즉, 버튼의 정보만 접근이 가능했는데 만약 버튼이벤트에서 다른 컴포넌트를 참조하고 싶은 경우에는 어떻게 접근할까?
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
class MyButtonActionListener implements ActionListener{
int count = 0;
MyPanle panel = null; // MyPanle레퍼런스 변수 생성
MyButtonActionListener(MyPanle in){ //생성자로 Mypanle객체를 가르키게함
panel = in;
}
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("button on!");
count++;
panel.label.setText("# of Count: "+count); //해당 객체의 text수정
}
}
class MyPanle extends JPanel{
JLabel label; //Lable변수 생성 함수가 여러번 호출되도 유지되도록
MyPanle(){
setBackground(Color.orange);
JButton but = new JButton("Button");
but.addActionListener(new MyButtonActionListener(this)); //자기자신을 생성자로 보냄
label = new JLabel(""+0);
add(but);
add(label);
}
}
public class HelloEventHandling extends JFrame{
public static void main(String []args){
new HelloEventHandling();
}
HelloEventHandling(){
setTitle("hello fkdl");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
MyPanle p = new MyPanle();
add(p);
setVisible(true);
}
}
- 다른 객체를 참조하고 싶다면 해당 객체의 레퍼런스 변수를 선언하고 생성자로 받아서 참조 가능하다
- 문제점음 해당 클래스들은 전부 호출하게 되는 클래스에 종속적이므로 다른 클래스 일반화가 불가능하다.
일반화가 불가능한 클래스는 가독성이 떨어지기 때문에 내부클래스로 전환하여 사용하면 보기 좋다.
class MyPanle extends JPanel{
JLabel label;
//private으로 묵어서 캡슐화
private class MyButtonActionListener implements ActionListener{
int count = 0;
MyPanle panel = null;
MyButtonActionListener(MyPanle in){
panel = in;
}
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("button on!");
count++;
panel.label.setText("# of Count: "+count);
}
}
MyPanle(){
setBackground(Color.orange);
JButton but = new JButton("Button");
but.addActionListener(new MyButtonActionListener(this));
label = new JLabel(""+0);
add(but);
add(label);
}
}
이렇게 사용도 가능하지만 가장 많이 사용되는 방식은 무명메서드와 람다식사용법이다!
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
class MyPanle extends JPanel{
MyPanle(){
setBackground(Color.orange);
JButton but = new JButton("Button");
ActionListener ac = new ActionListener(){ //인터페이스는 바로 객체로 만들 수 없기 때문에 해당 메서드를 무명메서드로 구현
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Anonymous class");
}
};
//람다식
ActionListener at = (ActionEvent e) -> System.out.println("악!");
but.addActionListener(ac); //리스너 등록
but.addActionListener(at);
add(but);
}
}
public class HelloEventHandling extends JFrame{
public static void main(String []args){
new HelloEventHandling();
}
HelloEventHandling(){
setTitle("hello fkdl");
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
MyPanle p = new MyPanle();
add(p);
setVisible(true);
}
}
- 코드가 많이 짧아지면서 간결해진다..!
- 또한 다른 컴포넌트를 참조한다고 해도 같은 클래스 내부에 있기 때문에 전역변수처럼 사용이 가능하다..!
주의
anonymous, 무명클래스를 사용할 때 로컬변수와 객체를 절대로 혼동해서는 안된다..!
class MyPanle extends JPanel{
MyPanle(){
setBackground(Color.orange);
JButton but = new JButton("Button");
int i = 0; // i는 생성자에서 호출되고 바로 사라지는 로컬변수
ActionListener ac = new ActionListener(){
int count = 0; // count는 ac기 가르키는 객체 안에 존재하는 멤버 필드이다.
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Anonymous class" + count + i);
count++;
}
};
but.addActionListener(ac);
add(but);
}
}
- 따라서 위에서 볼 수 있는 것처럼 무명클래스 내부에서 외부클래스의 변수들은 전역변수 처럼 사용가능하지만 i는 로컬변수로 선언되었기 때문에 접근은 가능하지만 변경이나 추가는 절대 불가능하다.(접근도 굳이할필요 없음 사용한다면 자동으로 final 상수처리)
- 하지만 but는 즉, JButton객체는 생성자가 종료되어도 살아있기 때문에 전역변수 처럼 호출이 가능하다.
- 마찬가지로 count변수도 멤버필드로 객체내부에 계속 존재하기 때문에 참조가능하다.
추가
class MyPanle extends JPanel{
int count = 0;
MyPanle(){
setBackground(Color.orange);
JButton but = new JButton("Button");
// 람다식 사용
but.addActionListener((e) -> {
System.out.println("Anonymous class" + count);
count++;
});
add(but);
}
}
- 맨 위의 코드와 같은 방식으로 동작하지만 길이는 10줄이상 짧아진 모습이다..
- 람다식은 명확한 정보들은 전부 생략할 수 있는데 여기서 생략된 정보들은 new ActionListener, public void actionPerformed, ActionEvent를 생략할 수 있다.
class MyPanle extends JPanel implements ActionListener{ //ActionListener상속
int count = 0;
MyPanle(){
setBackground(Color.orange);
JButton but = new JButton("Button");
but.addActionListener(this); //자기자신 전달
add(but);
}
@Override
public void actionPerformed(ActionEvent e) { //메서드 구현
System.out.println("Anonymous class" + count);
count++;
}
}
- this를 통해 자신의 이벤트리스너를 연결해준다.
- 하지만 위와 같이 코딩한다면 같은 리스너를 공유하기 때문에 버튼이 추가되어서 같은 리스너를 호출한다면 똑같은 곳을 가르키게 된다.
정리
위에서 사용한 여러가지 방법을 크게 정리하면 3가지 방법으로 정리 가능하다.
- 독립 클래스
- 이벤트 리스너를 완전한 클래스로 작성할때, 이벤트 리스너를 여러곳에서 사용할 때 적합
- 내부 클래스
- 클래스안에 멤버처럼 작성, 특정클래스에서만 사용할 때 적합
- 익명 클래스
- 클래스의 이름없이 간단히 리스너 작성, 코드가 간단한 경우 사용
종류
- ActionEvent(이벤트객체)
- JButton(이벤트 소스): 마우스나 enter키로 버튼 선택
- JMenuItem(이벤트 소스): 메뉴 아이템 선택
- JTextField(이벤트 소스): 텍스트 입력 중 enter키 입력
- KeyEvent(이벤트객체)
- Component(이벤트 소스): 키가 눌러지거나 눌러진 키가 떼어질 때
- MouseEvent(이벤트객체)
- Component(이벤트 소스): 마우스버튼이 눌러지거나 떼어질 때 등등..
이벤트 리스너
- 이벤트를 처리하는 코드, 클래스로 작성된다.
- 이벤트 작성을 위해 인터페이스를 제공하기 때문에 개발자가 리스너인터페이스의 추상메서드를 구현
- 이벤트가 발생하면 리스너인터페이스의 추상 메서드를 호출한다.
- 따라서 추상메서드의 개수에 따라 구현개수도 달라진다.
- ex) 이벤트 종류 Action / 리스너 인터페이스 ActionListener / 리스너 추상메서드 void actionPerformed(ActionEvent) / 메서드가 호출되는 경우 Action이벤트가 발생하는 경우
댓글남기기