글쓴이: Joshua Marinacci (NetBeans 엔지니어)
애플리케이션 구축 시 복수의 컴포넌트를 한데 묶어 다른 컴포넌트의 이벤트에 응답하도록 해야 할 경우가 종종 있으며, 또한 커스텀 컴포넌트를 구축할 때 커스텀 리스너 세트도 함께 구축하길 원할 수도 있다. 이는 상당히 올바른 '컴포넌트 에티켓'처럼 보인다. 어찌 되었건, 대부분의 javax.swing.* 컴포넌트는 이런 식으로 구축되지만, 그렇다고 해도 오로지 단순한 변화를 관찰하기 위해 새로운 종류의 리스너를 생성한다는 것은 골치 아픈 일이 아닐 수 없다. 게다가, 이 경우 클래스들이 단단히 결합되어 나중에 코드를 변경할 때 불안정한 상황이 발생할 수 있다. 따라서 더 효과적인 방법을 강구할 필요가 있으며, 이제부터 그 방법을 소개하고자 한다.
Swing은 Java Beans 패턴에 따라 속성명을 정의한다. 또한, Swing의 거의 모든 속성은 바운드 속성이며, 모든 JComponent 서브클래스는 addPropertyChangeListener 메소드를 가지고 있고, 대부분의 컴포넌트 속성은 이미 변경될 때 이벤트를 전송하도록 설정되어 있다. 예를 들어, JButton은 배경 색상 변경 시 리스너에 통지를 할 수 있다. JComponent 클래스 역시 커스텀 Swing 컴포넌트가 새로운 이벤트 클래스를 생성할 필요 없이 각자의 변경 이벤트를 전송할 수 있게 해주는 firePropertyChange 메소드를 가지고 있다. 그 결과, 새로운 코드는 최소화하고 새로운 인터페이스나 클래스를 전혀 사용하지 않고도 매우 손쉽게 컴포넌트들을 한데 묶을 수 있게 된다.
본 팁에서는 비트맵 타일 에디터 예제를 사용하도록 한다. 다음 이미지는 미완성 사용자 인터페이스(UI)를 보여준다.

그리드를 확대하는데 사용되는 "+" 버튼을 누르면 그리드 에디터 패널 자체가 확대되고, 그리드가 확대되면 작은 레이블에 그리드의 새 크기가 표시된다. 이 때, "+" 버튼 누르기, 그리드 리사이징, 레이블 변경 등을 링크하는 이벤트 체인이 구현되어야 한다. 사용자는 커스텀 이벤트(가령 GridSizeChangeEvents 또는 이와 비슷한 정도로 끔찍한 것)를 생성할 수는 있지만, 이는 결국 "Update Yourself" 요청과 같은 간단한 일에도 많은 힘이 든다. 따라서 그보다는, 이것이 PropertyChangeListener에서 어떻게 작동할지를 살펴보는 것이 더 나을 것이다.
에디터는 setGridWidth 메소드를 가지는 커스텀 JPanel이다. 그리드 폭이 변경될 때마다 패널은 내부 그리드 데이터를 재구축한 다음 그에 관한 "gridWidth" 속성 변경을 fire한다. 다음 코드 예제에 에디터 패널의 일부가 나와 있다.
public class TileBuilderEditorPanel extends JPanel { ... public void setGridWidth(int gridWidth) { this.oldGridWidth = this.gridWidth; this.gridWidth = gridWidth; rebuildGrid(); } private void rebuildGrid() { int[][] newgrid = new int[gridWidth][gridHeight]; ... this.gridcells = newgrid; repaint(); firePropertyChange("gridWidth", oldGridWidth, gridWidth); }}
다시 메인 클래스에서, 그리드 사이즈가 변경될 때마다 레이블을 업데이트해야 하는데, 아래와 같은 익명의 리스너 클래스에서는 이 작업이 간단하게 이루어진다.
editor.addPropertyChangeListener("gridWidth", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent propertyChangeEvent) { viewer.repaint(); grid_size.setText(realeditor.getGridWidth() + " x " + realeditor.getGridHeight()); }});
addPropertyChangeListener 메소드는 청취할 속성을 지정하는 인수를 취한다는 점에 유의할 것. 사용자가 "gridWidth" 이벤트를 원한다는 것을 컴포넌트에 알리면 리스너 내부의 특정 속성명을 굳이 확인하지 않아도 된다. 또한 이 예제의 경우 리스너는 "gridWidth" 값을 알 필요조차 없으므로 PropertyChangeEvent 오브젝트 내부의 데이터에 액세스하지 않아도 된다. 하지만 데이터가 꼭 필요할 경우에는 이를 이용할 수도 있다.
다음 단계는 그리드 폭을 조절하는 "+" 버튼의 액션 리스너를 생성하는 것으로, 본 예제에서는 이 버튼을 "width_plus" 버튼이라고 지칭한다. 리스너는 width_plus 버튼을 누를 때마다 그리드 폭을 1셀씩 확대한다. 다음 코드는 width_plus 버튼 이벤트를 위한 이벤트 핸들러를 나타낸다.
private void width_plusActionPerformed(java.awt.event.ActionEvent evt) {// TODO add your handling code here: editor.setGridWidth(realeditor.getGridWidth()+1);}
NetBeans IDE 5.5와 같은 통합 개발 툴의 경우 대부분 이벤트 핸들러를 생성하고 첨부할 수 있다. NetBeans는 이미 width_plusActionPerformed 메소드 스터브(method stub)를 작성한 다음 이를 실제 width_plus 버튼에 첨부해 두었는데, 이는 작성할 코드가 그만큼 줄어든다는 것을 의미한다.
width_plus 버튼을 누르면 해당 이벤트 핸들러가 editor.setGridwidth 메소드를 호출하여 내부 데이터 구조를 업데이트하고 자기 자신을 리페인트 처리한 다음 gridWidt 속성 이벤트를 fire한다. 익명의 리스너는 이벤트를 찾은 후 레이블을 업데이트한다. 이렇게 매우 간단하다.
PropertyChangeListener 클래스에 관한 자세한 내용을 보려면 다음 링크를 참조하기 바란다.
댓글을 달아 주세요
PropertyChangeListener 클래스에 대해서 자세한 설명 감사합니다. 링크까지 달아주시고 ㅎㅎ
2007/09/07 17:35쉬운 설명으로 읽으면 바로 쏫쏙 이해가 됩니다만.....
2007/09/10 22:38시간이 너무 없어요.
휴일에 시간내서 잘 읽어 보려구요.
좋은 정보 감사해요~
2007/09/19 04:01