자바프로그래밍 (OCJP 국제공인자격취득중심 ) 충북대학교정보통신공학부 최민
이벤트처리 지금까지 GUI 를구성하는 Component 의종류와이 Component 들을 Container 위에적절하게배치하기위한 LayoutManager 를학습하였음 앞에서만들었던 GUI 프로그램은모양만그럴듯할뿐, 실제 Button 을누르거나, Frame 우측상단의 X 표시를클릭해도아무런동작을하지않음이벤트처리가포함되어있지않기때문 이벤트 (Event) 프로그램과관계있는외부의사건으로서주로사용자에의해서일어나는데, 사용자가 Button 을마우스로누르거나, Frame 우측상단의 X 표시를클릭해서윈도우를닫는것, 마우스를움직이고, 키보드를누르는것등이이벤트의예 2
이벤트처리 위임형이벤트모델 (Delegation Event Model) 이러한각각의이벤트가발생했을때프로그램안에서적절한처리를해야하는데, jdk1.1 이후의자바에서는 GUI 를표시하는클래스와는별도로새클래스를만들어서이벤트처리를맡기는방법을사용합니다. 위임형이벤트모델인경우, 이벤트처리를위한프로그램에서는다음과같은내용이반드시포함되어야함 3
이벤트처리 1. import java.awt.event.*; 이벤트처리용클래스 import 2. c.addxxxlistener(new XXXClass( )); 이벤트소스 Component 와이벤트처리용리스너클래스연결 c : 이벤트를처리하고자하는 Component 에대한핸들 XXX : 처리하고자하는이벤트의종류 3. class XXXClass implements XXXListener { ( 또는 ) class XXXClass extends XXXAdapter { 이벤트처리용리스너클래스정의 XXXListener : 처리하고자하는이벤트와관련된메소드선언을가지고있는인터페이스 XXXAdapter : 처리하고자하는이벤트와관련된메소드선언을가지고있는클래스 4
이벤트처리 [NOTE] 이벤트처리클래스를작성할때원칙적으로자신이처리하고자하는이벤트에대한메소드가선언되어있는 Listener 인터페이스를 implements 해야합니다. 그런데메소드선언이여러개있는 Listener 인터페이스를 implements 하려면자신이오버라이드하고자하는메소드뿐만아니라, 다른메소드도모두오버라이드하여야하는불편이있기때문에두개이상의메소드가선언되어있는 Listener 인터페이스에대해서는대응하는 Adapter 클래스가제공됩니다. Adapter 클래스를 extends 하는경우에는모든메소드를오버라이드할필요는없기때문에실제구현하려는메소드만오버라이드하면됩니다. 5
이벤트처리 위에서제시한내용을프로그램으로구현하면다음과같은구조가됩니다. import java.awt.*; import java.awt.event.*; public class EventTest extends Frame { // 이벤트처리를위한패키지 import Button bclear; // 표시할 Component 선언 public static void main(string[ ] args) { /* 자기자신클래스에대한오브젝트생성 */ /* display( ) 메소드호출 */ EventTest(String title) { /* 각 Component 에대한오브젝트생성, 초기화 */ 6
이벤트처리 void display( ) { /* 각 Component 붙이기 */ bclear.addactionlistener(new ActionHandle( )); /* 이벤트소스에이벤트처리클래스등록 */ /* 화면에표시 */ class ActionHandle implements ActionListener { // 이벤트처리를위한클래스정의 public void actionperformed(actionevent e) { /* 이벤트가발생했을때의처리내용 */ 7
이벤트처리 그런데이와같이 Component 에대한오브젝트를생성, 표시하는 GUI 클래스와이벤트를처리하는클래스가분리되어있는경우, 처리내용에따라클래스가구성되므로알아보기쉬운프로그램을작성할수있다는장점이있는반면에, 클래스간에정보공유가어렵다는단점 예를들어 Label 이윗쪽에있고, Button 이아랫쪽에있어서아랫쪽의 Button 을누르면윗쪽 Label 에 Button 에지정된내용을출력하는프로그램을가정 ( 예 : 전자계산기 ). 이때, Button 을눌렀을때수행되는메소드는이벤트처리클래스에있고, Label 에대한핸들변수는 GUI 클래스에있을것이므로쉽게참조할수없음 8
이벤트처리 내부클래스 (inner class) 를 클래스를분리함으로써프로그램이간결하다는장점을그대로살리면서클래스간에정보공유를쉽게할수있는방법 JAVA 1.1 이후에내부클래스문법이추가된것은이러한이벤트처리방법과무관하지않음 내부클래스를이용해서이벤트를처리하는기본구조는다음과같음 9
이벤트처리 inner class 사용 import java.awt.*; import java.awt.event.*; // 이벤트처리를위한패키지 import public class EventTest extends Frame { Button bclear; // 표시할 Component 선언 public static void main(string[ ] args) { /* 자기자신클래스에대한오브젝트생성 */ /* display( ) 메소드호출 */ EventTest(String title) { /* 각 Component 에대한오브젝트생성, 초기화 */ 10
이벤트처리 inner class 사용 void display( ) { /* 각 Component 붙이기 */ bclear.addactionlistener(new ActionHandle( )); /* 이벤트소스에이벤트처리클래스등록 */ /* 화면에표시 */ class ActionHandle implements ActionListener { // 이벤트처리를위한클래스정의 public void actionperformed(actionevent e) { /* 이벤트가발생했을때의처리내용 */ 11
이벤트처리 내부클래스를이용한이벤트처리의기본적인구조도앞에서배운이벤트처리구조와거의똑같습니다. 다만, 이벤트처리클래스가 GUI 클래스안에있다는차이만있지요. 따라서, 이벤트처리클래스안에서는 GUI 클래스에있는멤버변수를마음대로쓸수있게되었습니다. 그런데이방법보다더간단한방법도있습니다. 바로 익명내부클래스 (ano nymous inner class)' 를이용하는것인데, 이경우프로그램의구조는다음과같습니다. 12
이벤트처리 anonymous class 사용 import java.awt.*; import java.awt.event.*; // 이벤트처리를위한패키지 import public class EventTest extends Frame { Button bclear; // 표시할 Component 선언 public static void main(string[ ] args) { /* 자기자신클래스에대한오브젝트생성 */ /* display( ) 메소드호출 */ EventTest(String title) { /* 각 Component 에대한오브젝트생성, 초기화 */ 13
이벤트처리 anonymous class 사용 void display( ) { /* 각 Component 붙이기 */ bclear.addactionlistener( new ActionListener( ) { public void actionperformed(actionevent e) { /* 이벤트가발생했을때의처리내용 */ ); // 이벤트처리클래스작성, 등록 /* 화면에표시 */ 14
이벤트처리 anonymous class 사용 이경우에는내부클래스에별도의이름을붙이지않고, 내용을통째로 add XXXListener 메소드의인수로넘겼습니다. 이방법은이벤트발생시처리내용이간단하고, 중복되는이벤트처리내용이없는경우에만사용하는것이좋습니다. 15
기본예제 이예제에서는 Button 과 TextField 로구성된 Frame 을만들고, Button 을누르면 TextField 의내용을지우고, Frame 우측상단의 X 표를클릭하면프로그램을종료시키는처리를하고있습니다. import java.awt.*; import java.awt.event.*; public class EventTest extends Frame { Button bclear; TextField tf; public static void main(string[ ] args) { EventTest et = new EventTest("Event 예제 "); et.display( ); EventTest(String title) { super(title); tf = new TextField( ); bclear = new Button(" 지워주세요 "); 16
기본예제 void display( ) { setlayout(new GridLayout(0, 1)); add(tf); add(bclear); bclear.addactionlistener(new ActionHandle( )); addwindowlistener(new WindowHandle( )); pack( ); setvisible(true); class ActionHandle implements ActionListener { public void actionperformed(actionevent e) { tf.settext(""); 17
기본예제 class WindowHandle implements WindowListener { public void windowclosing(windowevent e) { System.exit(1); public void windowopened(windowevent e) { public void windowclosed(windowevent e) { public void windowiconified(windowevent e) { public void windowdeiconified(windowevent e) { public void windowactivated(windowevent e) { public void windowdeactivated(windowevent e) { 18
기본예제 이예제는 ActionListener 와 WindowListener 를각각구현한이벤트처리용리스너클래스를내부클래스로작성하고, 메소드를오버라이드함으로써이벤트처리를하고있습니다. 사실, 한클래스에서인터페이스는두개이상지정하여모두구현할수있으므로이벤트처리용리스너를하나의클래스로통합하는것도가능합니다. 그런데 WindowListener 를구현한 WindowHandle 리스너클래스의경우, 인터페이스에선언되어있는모든메소드를오버라이드해야한다는문법적인이유때문에실제로오버라이드할필요없는메소드까지도모두한번씩이름을써주어야하는불편이있습니다 (48 ~ 53 행 ). 19
기본예제 이예제는앞의예제와기능은같지만, 처리방법이다릅니다. 즉, 이벤트처리용리스너클래스작성시리스너인터페이스를이용한것이아니라, 어댑터클래스를이용함으로써불필요한코드를줄일수있었습니다. import java.awt.*; import java.awt.event.*; public class EventTest2 extends Frame { Button bclear; TextField tf; public static void main(string[ ] args) { EventTest2 et = new EventTest2("Event 예제 2"); et.display( ); EventTest(String title) { super(title); tf = new TextField( ); bclear = new Button(" 지워주세요 "); 20
기본예제 void display( ) { setlayout(new GridLayout(0, 1)); add(tf); add(bclear); bclear.addactionlistener(new ActionHandle( )); addwindowlistener(new WindowHandle( )); pack( ); setvisible(true); class ActionHandle implements ActionListener { public void actionperformed(actionevent e) { tf.settext(""); class WindowHandle extends WindowAdapter { public void windowclosing(windowevent e) { System.exit(1); 21
이예제에서는 WindowListener 인터페이스대신에 WindowAdapter 클래스를이용함으로써프로그램을좀더간단하게만들수있었습니다. 이처럼모든두개이상의메소드가정의되어있는리스너인터페이스에대해서대응하는어댑터클래스를이용하면좀더간결한코드를만들수있습니다. 다만, 이런경우에한클래스에서두개이상의 Adapter 클래스를상속받을수는없습니다. 22
기본예제 이예제도앞의예제와기능은같지만, 처리방법이다릅니다. 즉, 이벤트처리용리스너클래스작성시리스너클래스를별도로작성한것이아니라, addwindowlistener() 메소드호출시클래스의내용을직접넘겨주었습니다. import java.awt.*; import java.awt.event.*; public class EventTest3 extends Frame { Button bclear; TextField tf; public static void main(string[ ] args) { EventTest2 et = new EventTest3("Event 예제 3"); et.display( ); EventTest(String title) { super(title); tf = new TextField( ); bclear = new Button(" 지워주세요 "); 23
기본예제 void display( ) { setlayout(new GridLayout(0, 1)); add(tf); add(bclear); bclear.addactionlistener(new ActionHandle( )); addwindowlistener( new WindowAdapter( ) { public void windowclosing(windowevent e) { System.exit(1); ); pack( ); setvisible(true); class ActionHandle implements ActionListener { public void actionperformed(actionevent e) { tf.settext(""); 24
기본예제 void display( ) { setlayout(new GridLayout(0, 1)); add(tf); add(bclear); bclear.addactionlistener(new ActionHandle( )); addwindowlistener( new WindowAdapter( ) { public void windowclosing(windowevent e) { System.exit(1); ); pack( ); setvisible(true); class ActionHandle implements ActionListener { public void actionperformed(actionevent e) { tf.settext(""); 25
이예제에서는이벤트처리용리스너클래스를별도로작성하지않고, addwindowlistener( ) 메소드호출시직접리스너클래스의내용을넘겨주었음 (31 ~ 37 행 ). 이렇게작성하면코드의길이가짧아진다는장점이있지만, 이벤트처리용리스너클래스를반복해서사용할수없다는단점 26
이벤트의종류 앞서이벤트처리를위한프로그램의구조에대해서공부아였진행하셔도좋을그렇다면이벤트에는어떤종류가있으며, 어떤방식으로처리해야할까요? 다음표는중요한이벤트의종류와이벤트를처리하는클래스작성시상속받아야하는인터페이스 ( 또는클래스 ) 이름, 그리고그안에정의되어있는메소드의형태를정리한것입니다. 27
분류 Listener interface (Adapter class) 메소드명 ( 파라미터 ) 이벤트소스 Component Action ActionListener actionperformed (ActionEvent) Button List MenuItem TextField Item ItemListener itemstatechanged (ItemEvent) CheckBox Choice List Mouse motion MouseMotionListener (MouseMotionAdapter) mousedragged(mouseevent) mousemoved(mouseevent) Component Mouse MouseListener (MouseAdapter) mousepressed(mouseevent) mousereleased (MouseEvent) mouseentered(mouseevent) mouseexited(mouseevent) mouseclicked(mouseevent) Component Key KeyListener (KeyAdapter) keypressed(keyevent) keyreleased(keyevent) keytyped(keyevent) Component Window WindowListener (WindowAdapter) windowclosing(windowevent) windowopened(windowevent) windowiconified (WindowEvent) windowdeiconified (WindowEvent) windowclosed(windowevent) windowactivated (WindowEvent) windowdeactivated (WindowEvent) Window Text TextListener SCJP 5.0 대비강좌 textvaluechanged (TextEvent) TextComponent 28
이벤트의종류 예를들어, Frame 위에서마우스를클릭하는이벤트를처리하고자한다면어떻게해야할까요? 위의표에있는 Mouse 관련이벤트중에서관련메소드를찾아보면 mouseclicked( ) 메소드가있군요. 따라서, 이벤트처리를위한리스너클래스에서는 mouseclicked( ) 메소드를오버라이드하면되겠고, Mou se 관련이벤트중에서다른이벤트는처리하지않을것이므로그클래스옆에는 implements MouseListener 라고쓰지말고, extends MouseAdapter 라고쓰면되겠군요. 그러면이벤트소스클래스인 Frame 클래스에어떻게이벤트리스너클래스를등록할까요? 바로 addmouselistener( ) 메소드를이용하여지금만든리스너클래스의오브젝트를인수로넘겨주면되겠군요. 이와같이이벤트를처리하는클래스를작성할때위의표는자주참조하게되므로잘기억해두세요. 29