Java 정리 21
11 Dec 2018
Reading time ~9 minutes
Java 정리 21 - AWT Event Handling
Event Handling
- 사용자의 동작에 따른 처리코드를 만들어두고 그 코드를 실행하는 것
- AWT와 Swing에서 같이 사용할 수 있다.
- java.awt.event 패키지에서 관련 클래스, interface 제공
- interface가 하나 이상의 추상 method를 가진다면 사용의 편의성을 위해 ~Adapter Class를 제공
이벤트 처리를 Is-A 관계로 처리하는 방법
-
디자인과 이벤트 처리를 하나의 클래스안에서 처리한다.
- 값에 대한 사용이 편하다.
- 한 클래스에서 상속으로 Frame을 받고 구현으로 OOOListener를 처리
- 이벤트처리와 관련있는 Component를 선언
- Component를 생성
- 컴포넌트가 이벤트를 발생할 수 있도록 등록
// 1. 한 클래스에서 상속으로 Frame을 받고 구현으로 OOOListener를 처리
public class Test extends Frame implements ActionListener { ... }
// 2. 이벤트처리와 관련있는 Component를 선언
Button btn;
// 3. Component를 생성
public Test() {
btn = new Button();
}
// 4. 컴포넌트가 이벤트를 발생할 수 있도록 등록
// ooo.addOOOListener(이벤트가발생했을때처리될객체);
btn.addActionListener(this);
// 이벤트가 등록되고 이벤트가 발생하면 입력된 객체의 Override된 추상 method가 호출
// 5. btn 컴포넌트의 이벤트 발생시 Override된 메소드 수행
// ActionListner의 abstract method Override
@Override
public void actionPerformed(ActionEvent ae) {
// 이벤트가 발생했을 때 처리
}
/**
* ActionEvent를 사용한 Is-A 관계로 이벤트를 처리하는 방법
* 디자인과 이벤트 처리를 하나의 클래스안에서 처리한다.
* (값에 대한 사용이 편하다.)
*/
// 1. 상속으로 Frame을 처리하고, 구현으로 Event Handling 객체를 처리
@SuppressWarnings("serial")
public class EventHandlingIsA extends Frame implements ActionListener {
// 2. 이벤트 처리와 관련이 있는 Component 선언
private Button btn;
public EventHandlingIsA() {
super("Is-A 관계로 이벤트 처리방법");
// 3. Component 생성
btn = new Button("클릭");
// 4. 생성된 컴포넌트가 이벤트를 발생시킬 수 있도록이벤트에 등록
btn.addActionListener(this);
// Arguments로 입력된 객체의 Override된 method가 호출된다.
Panel panel = new Panel();
panel.add(btn);
// 5. 생성된 컴포넌트를 배치
add(BorderLayout.CENTER, panel);
// 6. 윈도우 컴포넌트의 크기를 설정
setBounds(400, 200, 300, 300);
// 7. 사용자에게 보여주기 (가시화)
setVisible(true);
// 8. 종료처리
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
dispose();
}
});
}
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("버튼이 클릭되었습니다.");
dispose();
}
public static void main(String[] args) {
new EventHandlingIsA();
}
}
이벤트처리를 Has-A 관계로 처리하는 방법
-
디자인과 이벤트처리 코드를 분리할 수 있다.
- 클래스가 늘어난다.
- 값 넘기기 어렵다.
- 에러처리가 복잡해진다.
- Design 클래스
- Frame 상속받는 클래스 생성
- 이벤트 관련 Component를 선언
- 컴포넌트 생성
- 이벤트 처리객체 생성하고 Has-A관계 설정
- 이벤트 등록
// 1 . Frame 상속받는 클래스 생성
public class Test extends Frame { ... }
// 2. 이벤트 관련 Component를 선언
Button btn;
// 3. 컴포넌트 생성
public Test() {
btn = new Button();
}
// 4. 이벤트 처리객체 생성하고 Has-A관계 설정
TestImpl ti = new TestImpl(this);
// 5. 이벤트 등록
btn.addActionListener(ti);
- Event 클래스
- OOOListener를 구현하는 클래스 생성
- 디자인 클래스를 Has-A 관계로 가지고 사용할 수 있도록 선언
- 생성자에서 디자인 클래스를 받는다.
- 추상 method Override
// 1. OOOListener를 구현하는 클래스 생성
public class TestImpl implements ActionListener { ... }
// 2. 디자인 클래스를 Has-A 관계로 가지고 사용할 수 있도록 선언
private Test t;
// 3. 생성자에서 디자인 클래스를 받는다.
public TestImpl (Test t) {
this.t = t;
}
// 4. 추상 method Override
public void actionPerformed(ActionEvent e) {
// 이벤트가 발생했을 때 처리
}
/**
* Has-A 관계의 이벤트 처리
* 디자인과 이벤트처리 코드를 분리하여 코드의 복잡도를 낮출 수 있다.
*/
// 1. 윈도우 컴포넌트를 상속
@SuppressWarnings("serial")
public class Design extends Frame {
// 2. 이벤트처리와 관련있는 Component를 선언
private Button btn;
public Design() {
super("디자인");
// 3. 컴포넌트 생성
btn = new Button("클릭");
// 4. 배치
Panel panel = new Panel();
panel.add(btn);
add(BorderLayout.CENTER, panel);
// 5. 이벤트 등록
// 이벤트 처리 객체 생성하고 Has-A 관계설정
EventHandlingHasA ehha = new EventHandlingHasA(this);
System.out.println("생성된 디자인 객체 "+this);
// 컴포넌트에 이벤트를 등록
// 버튼에서 이벤트가 발생하면 ehha객체의 Override된 method에서
// 이벤트를 처리
btn.addActionListener(ehha);
// 6. 윈도우 크기 설정
setBounds(100,100,400,300);
// 7. 가시화
setVisible(true);
// 8. 종료처리
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
dispose();
}
});
}
public Button getButton() {
return btn;
}
public static void main(String[] args) {
new Design();
}
}
/**
* Has-A 관계로 이벤트 처리
*/
// 1. 이벤트 처리 객체를 구현
public class EventHandlingHasA implements ActionListener {
// EventHandlingHasA는 Design을 가지고 있다.
// 2. Has-A 관계로 저장하고 사용할 객체 선언
private Design design;
public EventHandlingHasA(Design design) {
this.design = design;
}
@Override
// 3. abstract method를 Override
public void actionPerformed(ActionEvent e) {
// 이벤트가 발생했을 때 처리할 코드
System.out.println("전달받은 Design객체 : "+design);
design.dispose();
}
}
이벤트처리를 inner class로 처리하는 방법
-
디자인에 해당하는 이벤트처리 코드가 매우 적거나 특정디자인에 해당하는 이벤트만 처리할 때
- 디자인 클래스안에서 여러 이너 클래스로 이벤트를 구분하여 처리가능
- 윈도우 컴포넌트를 상속
- 이벤트 처리와 관계된 컴포넌트 선언
- 생성
- 이벤트를 처리하기 위한 inner class를 작성
- 추상 메소드 Override
- 이벤트 등록
// 1. 윈도우 컴포넌트를 상속
public class Test extends Frame { ... }
// 2. 이벤트 처리와 관계된 컴포넌트 선언
Button btn;
// 3. 생성
public Test() {
btn = new Button("버튼");
}
// 4. 이벤트를 처리하기 위한 inner class를 작성
public class Inner implements ActionListener {
// 5. 추상 메소드 Override
@Override
public void actionPerformed(ActionEvent e) {
// 이벤트가 발생했을 때 처리할 코드
}
}
// 6. 이벤트 등록
// Inner Class를 객체화
Test.Inner in = this.new Inner();
// Component가 이벤트를 발생할 수 있도록 등록
btn.addActionListener(in);
/**
* inner class를 사용한 이벤트 처리 :
* 디자인에 해당하는 이벤트처리 코드 코드가 매우 적거나
* 특정디자인에 해당하는 이벤트만 처리할 때
*/
// 1. 윈도우 컴포넌트를 상속
public class EventHandlingInnerClass extends Frame {
// 2. 이벤트처리 관련 컴포넌트를 객체화
private Button btn;
public EventHandlingInnerClass() {
super("Inner class로 이벤트 처리");
// 3. 컴포넌트 생성
btn = new Button("클릭");
// 4. 배치
Panel panel = new Panel();
panel.add(btn);
add(BorderLayout.CENTER, panel);
// 5. 이벤트 등록
// inner class 생성.
EventHandlingInnerClass.InnerActionImpl iai =
this.new InnerActionImpl();
// 컴포넌트에 이벤트에 등록
btn.addActionListener(iai);
// 6. 윈도우 크기 설정
setBounds(100, 100, 300, 300);
// 7. 가시화
setVisible(true);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
dispose();
}
});
}
public Button getBtn() {
return btn;
}
//////////////// inner class 시작 //////////////////
// 5-1. inner class로 이벤트처리 리스너를 구현
public class InnerActionImpl implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("버튼을 클릭하였습니다. "
+ "o(>.<)o 꺄~~~");
// 안쪽클래스에서는 바깥클래스의 자원을 내 것처럼 사용가능
dispose();
}
}
//////////////// inner class 끝 ////////////////////
public static void main(String[] args) {
new EventHandlingInnerClass();
}
}
Anonymous inner class
- 이벤트 처리 코드의 재사용성이 낮은 방법
- Window Component를 상속
- Component를 선언
- 생성
- 이벤트 등록
// 1. Window Component를 상속
public class Test extends Frame { ... }
// 2. Component를 선언
Button btn;
// 3. 생성
public Test() {
btn = new Button();
}
// 4. 이벤트 등록
// anonymous inner class
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 이벤트가 발생했을 때 처리할 코드
}
});
// anonymous inner class로 이벤트 처리
// 1. 윈도우 컴포넌트 상속
public class EventHandlingAnonymous extends Frame {
// 2. 이벤트처리에 사용할 Component 선언
private Button btn;
public EventHandlingAnonymous() {
super("click!");
// 3. 컴포넌트 생성
btn = new Button("버튼");
// 4. 배치
Panel panel = new Panel();
panel.add(btn);
add("Center",panel);
// 5. 이벤트 등록 : anonymous inner class
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("버튼 클릭!");
}
});
// 6. 윈도우 크기 설정
setBounds(200, 100, 400, 200);
// 7. 가시화
setVisible(true);
}
public Button getBtn() {
return btn;
}
public static void main(String[] args) {
new EventHandlingAnonymous();
}
}
OOOAdapter class가 사용되어야 하는 이유
- OOOListener interface를 구현한 class는 사용하지 않는 method일지라도 모두 Override 해야한다.
// OOOAdapter class가 사용되어야 하는 이유
@SuppressWarnings("serial")
public class UseWindowEvent extends Frame implements WindowListener {
public UseWindowEvent() {
super("윈도우 이벤트 연습");
setBounds(100, 100, 400, 400);
setVisible(true);
addWindowListener(this);
}
public static void main(String[] args) {
new UseWindowEvent();
}
// WindowListener interface를 구현한 class는
// 사용하지 않는 method일지라도 모두 Override 해야한다.
@Override
public void windowOpened(WindowEvent e) {
System.out.println("windowOpened");
}
@Override
public void windowClosing(WindowEvent e) {
System.out.println("windowClosing");
dispose();
}
@Override
public void windowClosed(WindowEvent e) {
// windowClosing에서 dispose()가 호출되면 그때 windowClosed가 호출
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");
}
}
Adapter class
-
이벤트를 처리하는 interface가 추상 method를 하나 이상 가지고 있을 때 interface를 미리 구현한 class들
- class의 내용들은 empty
- 사용상의 편의성이 증대
@SuppressWarnings("serial")
public class UseWindowAdapter extends Frame {
public UseWindowAdapter() {
super("윈도우 이벤트 연습");
setBounds(100, 100, 400, 400);
setVisible(true);
UseWindowAdapter.WindowImpl wi = this.new WindowImpl();
addWindowListener(wi);
}
public static void main(String[] args) {
new UseWindowAdapter();
}
// WindowAdapter class를 상속받으면 필요한 method만 Override하면 된다.
public class WindowImpl extends WindowAdapter {
@Override
public void windowClosing(WindowEvent e) {
dispose();
}
}
}
이벤트처리 구현 예시(01)
- 입력버튼이 클릭되면 TextField의 입력값을 가져와서 TextArea에 추가할 것
- “입력” 버튼클릭 -> ActionEvent
- ActionListener, 등록은 addActionListener(처리될 곳)
- TF에서 값 가져오는건 TextComponent 클래스의 getText 사용
- TA에 TextArea의 append를 사용해서 가져온 text를 추가
- Is-A 구현시
// ActionListener를 사용한 이벤트 처리
// is-a 관계로 이벤트를 처리
@SuppressWarnings("serial")
public class UseActionListener extends Frame implements ActionListener {
// 다른 메소드에서 사용해야 하므로 지역변수에서 인스턴스변수로 옮김
private TextField tfName;
private TextArea taDisplay;
public UseActionListener() {
super("ActionEvent 사용");
Label lblName = new Label("이름");
Button btnAdd = new Button("추가");
Button btnClose = new Button("종료");
Panel panelNorth = new Panel();
tfName = new TextField(20);
taDisplay = new TextArea();
panelNorth.add(lblName);
panelNorth.add(tfName);
panelNorth.add(btnAdd);
panelNorth.add(btnClose);
setLayout(new BorderLayout());
add(BorderLayout.NORTH, panelNorth);
add(BorderLayout.CENTER, taDisplay);
// 이벤트 등록
btnAdd.addActionListener(this); // 버튼에서 이벤트 등록(클릭)
tfName.addActionListener(this); // 텍스트필드에서 이벤트 등록(엔터)
btnClose.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
setBounds(200, 150, 400, 250);
setVisible(true);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
dispose();
}
});
}
public static void main(String[] args) {
new UseActionListener();
}
@Override
public void actionPerformed(ActionEvent e) {
// TextField의 값을 받아와서, TextArea에 추가
String name = tfName.getText();
if (!name.isEmpty()) { // 입력문자열이 비어있지 않다면
taDisplay.append(name+"\n");
// TextField의 값을 초기화
tfName.setText("");
}
// 커서를 TextField에 재설정
tfName.requestFocus();
}
}
이벤트처리 구현 예시(02)
- List 아이템을 클릭하면 라벨에 해당 아이템의 정보를 표시할 것
-
ItemEvent 처리
- ItemListener 인터페이스 사용
-
리스트컴포넌트.addItemListener 사용
- 리스트 클릭 했을 때 getSelectedItem()로 선택한 item 받기
- 리스트를 선택했을 때 선택한 리스트의 인덱스를 얻는 getSelectedIndex()도 존재
- getItem(int index)랑 같이 사용가능
- Is-A 구현시
/**
* ItemEvent를 처리하는 ItemListener의 사용
*/
@SuppressWarnings("serial")
public class UseItemListener extends Frame implements ItemListener {
private List list;
private Label lbl;
public UseItemListener() {
list = new List();
list.add("이재찬/27");
list.add("정택성/26");
list.add("공선의/27");
lbl = new Label("이름 : 나이 : ");
add(BorderLayout.CENTER, list);
add(BorderLayout.SOUTH, lbl);
list.addItemListener(this);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
dispose();
}
});
setBounds(300, 200, 300, 300);
setVisible(true);
}
public static void main(String[] args) {
new UseItemListener();
}
@Override
public void itemStateChanged(ItemEvent e) {
// 리스트의 아이템이 선택되면 선택한 아이템을 가져와서
// 라벨의 값 변경
String[] nameAndAge = list.getSelectedItem().split("/");
String name = nameAndAge[0];
String age = nameAndAge[1];
// 긴 문자열이기 때문에 StringBuilder로 처리
StringBuilder viewData = new StringBuilder();
viewData.append("이름 : ").append(name).append(" 나이 : ").append(age);
lbl.setText(viewData.toString());
}
}
숙제풀이
-
Has-A 관계로 구현