AWT Event

Java나 다른 언어에서 Event라는 것은 특정 사건의 발생함을 의미합니다. 사용자가 행동하는 모든 것이 프로그램에서 이벤트로 동작이 될 수 있습니다. 버튼을 마우스로 클릭하거나 키보드로 입력을 받거나, 창을 이동하거나 마우스를 단순히 이동하는 것 등이 Event라고 합니다. 그리고 그 Event가 발생했을때 어떻게 프로그램을 동작시킬지 처리하는 것을 이벤트 핸들링(Event Handling)이라고 합니다. 자바에서 Event가 발생했을때 처리가능하도록 Interface를 정의해두었는데, 그 인터페이스 이름들은 뒤에 ~Listener가 붙게 됩니다. 예를 들어 Mouse에 관한 이벤트 처리 인터페이스는 MouseListener, 그리고 윈도우 창에 관한 이벤트를 처리할 수 있는 인터페이스는 WindowListener입니다. 우리는 Event가 발생했을때 어떻게 다뤄질 수 있는지 WindowListener를 통해서 알아보도록 하겠습니다.

 

EventHandling - 이벤트 처리 방법

이벤트 핸들링 위해 ~Listener라는 인터페이스가 존재한다고 했었죠. 이것을 implements로 상속받아 어떤 이벤트를 처리할지 구현하면 됩니다. 그래서 상속받은 인터페이스가 구현이 되면 그 이벤트에 영향을 받을 Component에 추가해주면 됩니다. 대부분읜 여기서는 WindowListener를 통해서 어떻게 처리하는지 알아본다고 했었죠. 그래서 WindowListener를 implements해보고 내부를 채워보도록 합시다.

WindowHandler 클래스를 만들고 WindowListener를 implements해봅시다.

class WindowHandler implements WindowListener{
}

 

이렇게 되면 WindowHandler에 밑줄이 쫙 그어지는데, 마우스를 가져다대면 아래에 Add unimplemented methods가 생십니다. 이것을 클릭해서 모든 메소드를 구현해줍니다.

 

아래처럼 아직은 메소드가 구현이 되어있지 않은 빈 클래스입니다. 

class WindowHandler implements WindowListener{

	@Override
	public void windowOpened(WindowEvent e) {}
	@Override
	public void windowClosing(WindowEvent e) {}
	@Override
	public void windowClosed(WindowEvent e) {}
	@Override
	public void windowIconified(WindowEvent e) {}
	@Override
	public void windowDeiconified(WindowEvent e) {}
	@Override
	public void windowActivated(WindowEvent e) {}
	@Override
	public void windowDeactivated(WindowEvent e) {}
}

 

AWT의 Frame에 관해서 배우셨다면 Frame의 X버튼을 클릭해도 닫아지지 않습니다. 이유는 우리가 X버튼을 눌렸을때 발생하는 이벤트에 대해서 처리해주지 않았기 때문입니다. 이 이벤트는 이벤트에 관한 정보를 WindowEvent에 담아서  WindowListener의 windowClosing() 메소드를 호출합니다. 아래의 코드를 구현하면 프로그램이 종료됩니다.

	@Override
	public void windowClosing(WindowEvent e) {
		Window w=e.getWindow();	
		w.setVisible(false);	//frame을 invisible	
		w.dispose();			//메모리 제거
		System.exit(0);					//시스템 종료
	}

 

WindowEvent에는 이벤트가 발생한 Component를 알 수 있습니다. 여기서는 단일 Frame을 사용하기 때문에 Frame을 구분할 필요가 없지만 만약 여러 프레임을 사용해서 같은 WindowHandler를 사용한다면 getComponent()를 통해서 Frame을 구분할 수 있습니다. 이벤트 클래스는 ComponentEvent를 상속받기 때문에 WindowEvent객체 뿐만 아니라 ComponentEvent를 상속받은 모든 Event클래스는 getComponent()를 통해서 객체를 구분할 수 있습니다.

해당하는 Window객체를 getWindow() 메소드로 가져올 수 있습니다. 

이벤트 리스너가 구현이 되었다면 이제 컴포넌트에 연결을 지어야겠죠? 해당 컴포넌트의 add~Listener()  메소드가 그 역할을 합니다. 여기서는 addWindowListener() 메소드입니다.

	public static void main(String[] ar) {
		Frame frame=new Frame("reakwon");
		frame.setSize(500,300);		//크기 설정
		frame.setLocationRelativeTo(null);	//중앙위치
		
		frame.addWindowListener(new WindowHandler());	//이벤트 설정
		
		frame.setVisible(true);	//Frame visible
	}

 

그리고 난 후에 프로그램을 실행시켜보고 X버튼을 누르면 프로그램이 종료되는 것을 확인할 수 있습니다. 

 

단순히 종료시키는 것이 아니라 "정말 종료하시겠습니까?" 대화창을 열어 확인해볼 수도 있겠죠. 여러분이 응용하기 나름입니다.

이벤트 리스너를 연결하는 방법

1. Component가 Listener를 implements하여 자신을 add하는 방법

이벤트를 연결하는 것중에는 아예 이벤트 리스너를 해당 Component가 상속받는 형태로 구현할 수도 있습니다. 아래의 방식처럼 말이죠.


class MyFrame extends Frame implements WindowListener{

	public MyFrame(String title) {
		super(title);
		this.setSize(500,300);
		this.setLocationRelativeTo(null);
		this.addWindowListener(this);	//WindowListener를 이곳에서 구현
		this.setVisible(true);
	}
	@Override
	public void windowOpened(WindowEvent e) {}
	@Override
	public void windowClosing(WindowEvent e) {	
		this.setVisible(false);	//frame을 invisible	
		this.dispose();			//메모리 제거
		System.exit(0);					//시스템 종료
	}
	@Override
	public void windowClosed(WindowEvent e) {}
	@Override
	public void windowIconified(WindowEvent e) {}
	@Override
	public void windowDeiconified(WindowEvent e) {}
	@Override
	public void windowActivated(WindowEvent e) {}
	@Override
	public void windowDeactivated(WindowEvent e) {}
}




 

 

2. 익명 클래스(Anonymouse class) 방식으로 연결

또는 익명 클래스로 바로 리스너를 구현해서 집어넣는 방법으로도 이벤트 리스너 연결이 가능합니다.

class MyFrame extends Frame{

	public MyFrame(String title) {
		super(title);
		this.setSize(500,300);
		this.setLocationRelativeTo(null);
		this.addWindowListener(new WindowListener() {
			@Override
			public void windowOpened(WindowEvent e) {}
			@Override
			public void windowClosing(WindowEvent e) {
				
				MyFrame.this.setVisible(false);	//frame을 invisible	
				MyFrame.this.dispose();			//메모리 제거
				System.exit(0);					//시스템 종료
			}
			@Override
			public void windowClosed(WindowEvent e) {}
			@Override
			public void windowIconified(WindowEvent e) {}
			@Override
			public void windowDeiconified(WindowEvent e) {}
			@Override
			public void windowActivated(WindowEvent e) {}
			@Override
			public void windowDeactivated(WindowEvent e) {}
		});	
		this.setVisible(true);
	}
	
}

 

2.1익명 클래스시 자주하는 실수

1. 익명 클래스 방식으로 구현할때 주의해야할 점은 this가 WindowListener 자체를 가리키는 객체를 말합니다. 그래서 Component를 가리키게 하려면 앞에 클래스명을 명시적으로 알려줘야합니다.

2. 아래의 코드가 에러가 발생하는 이유는 무엇일까요? 

	public MyFrame(String title) {
		//...생략...//
		int count=0;
		this.addWindowListener(new WindowListener() {
		
			@Override
			public void windowClosing(WindowEvent e) {
				count++;
                //...생략...//
			}
			
		});	
	}

 

저는 분명 count가 하나 증가하기를 기대하고 코드에 넣었는데, 빨간 밑줄인 에러가 발생합니다. 마우스를 가져다 대보니 final을 쓰라고 하네요.

 

왜 일까요? 여러분은 두가지를 간과하고 계실 겁니다. 

첫번째는 코드가 순차적으로 실행될 것이라는 믿음, 그리고 두번째 지역변수는 함수가 끝나면 정리된다는 것을 까먹은 것이죠. 위 이벤트 리스너의 실행은 이벤트가 발생했을때의 수행하는 것이지, 순차적 실행이 아닙니다. 그리고 생성자가 종료된 후에는 count라는 지역변수는 메모리에 없습니다. 그래서 증가시킬 수가 없죠. 

해결방법은 지역변수의 count를 final로 선언하여 값을 변경하지 않는것이고, 값을 변경하길 원한다면 아예 클래스(여기서는 MyFrame)의 멤버 변수로 선언하는 방법입니다.

지금까지 이벤트의 개념과 이벤트 핸들링을 하는 방법을 설명했는데, 이것을 일일히 설명하는 것보다는 아예 프로그램을 만들면서 그때그때 설명하는 것이 더 기억에 많이 남고 재밌을 것 같습니다. 그래서 시간이 된다면 프로그램 자체를 만들면서 설명해보도록 하겠습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,