Java Swing을 한다면 1
Java Swing에 대해 어떻게 GUI를 배워나가는 지에 대한 이야기를 해보고 싶었다.
물론 어느 순서가 바른 순서인지는 나도 모르고, 단지 내가 해온 순서는 이렇다는 이야기를 해보고 싶어서 글을 쓴다.
Basic GUI
Java Basic GUI는
- 창을 띄우고
- Button을 띄우고,
- Text를 입력받는 등의
간단한 UI 코딩이다.
- Model, Editor, Renderer의 개념을 몰라도 괜찮고,
- NetBeans 같은 도구를 이용하여 보다 쉽게 GUI를 그려나갈 수 있고,
- 그에 대한 Event Handling으로 간단한 GUI Application을 만들어 나가는 단계이다.
with LayoutManager
Java Basic GUI with LayoutManager
는 사실 단계가 어느 지점이 적합할지는 잘 모르겠는데,
이정도가 적당할 것이라고 생각해서 여기 써본다.
Java는 유동적인 Layout을 위해 LayoutManager
라는 재미난 개념을 도입했다. 이는 말 그대로 Component
들을 배치시켜주는 것이다.
GridLayout
은 Grid 형식으로JComponent
를 배치시켜주고,FlowLayout
은 줄줄이 Component가 나오게 해주는 식이다.
Container
에 Component
를 추가해놓고, LayoutManager
만 바꿈으로써 Component
의 배치가 달라지게 되는 것이다.
이는 종래 UI 제작의 모든 Component
의 Bound[x, y, width, height]를 지정하던 방식인 NullLayout
에 비해 상당히 유동적인 UI 구성이 가능하게 했으며,
무엇보다도 UI를 그리는 도구 없이도 간단한 UI를 만들 수 있을 뿐더러 합으로 복잡한 UI 구성도 가능하게 해주었다.
NetBeans의 GroupLayout
의 등장으로 ui designer로 FreeDesign이 가능해졌고, 그 기능으로 많이 개발하는 것도 같다. 하지만 GroupLayout
는 도구가 생성하는 코드라서 굉장히 코드가 복잡해진다는 단점이 있다.
LayoutManager
를 구현하여 자신이 만든 LayoutManager
로 Component의 배치를 직접 제어해보는 것도 상당한 도움이 된다. 물론, 빠르게 구현해야 할 때에는 NetBeans로 Design해서 Design 코드를 복사해서 Eclipse로 편집하기는 하지만, 간단한 UI를 설계하거나 배치에 모종의 규칙을 갖는 UI 설계의 경우 기존에 존재하는 Layout 조합만큼 좋은 것은 없다.
특히 Mustang(Java6)의 SpringLayout
은 뭔가 재밌어보인다. 하지만 아직 깊에 안 봐서 잘 모르겠다.
with Advanced Event Handler
Java는 굉장히 많은 EventHandler
를 지원한다.
Event Handling을 한다는 것은 해당 Component
에서 발생한 Event에 대해 어떤 일을 수행할 것인지에 대해 코드를 작성하는 것이다.
- Handler는 Event를 처리하기 위해 기술된 코드 집합, 즉 함수이고,
- 보통
{EventName}Listener
라는 이름 구조를 갖는다. - 어떤 Event에 대해서 그 Event를 기다리는 자(
Listener
)라는 의미로 대부분 Event를 처리하기 위한 함수 원형이 기술된interface
이다.
즉, 이걸 구현하면 함수 객체가 되는 것이고, 이것을 해당 Component의 EventHandler로 등록해주는 것이다.
보통 쉽게 접하는 것은 ActionListener
, KeyListener
, MouseListener
, FocusEvent
정도 인데, 이것들 이외에도 ComponentEvent
나 HierarchyEvent
, AncestorEvent
, ContainerEvent
, PropertyChangeEvent
등이 있다.
ComponentEvent
는 해당 Component가 나타났나, 사라졌나에 대한 Event 등을 포함하고 있고,ContainerEvent
는 자신에게 어떤 다른JComponent
가 추가됬나, 제거됬나 등에 대한 Event가 포함되어있고,AncestorEvent
와HierarchyEvent
는 부모 쪽에서 모종의 변경이 일어났을 때에 대한 Event가 포함되어있다.
위의 것들은 그냥 그러려니 하면서, 실제로 쓸 일이 없어서 잘 안 쓰는 그런 종류일 수 있다. 하지만 PropertyChangeEvent
는 쓸만한, 그리고 재미있는 event이다.
Property
는 Component의 상태를 표현하기 위해 필요한 정보들로, Visible
, Enable
등이 있다. NetBeans에서는 아예 Property라고 해서 Table로 이를 편집할 수 있게 해준다.
보통 set{PropertyName}
으로 설정하고, [get|is]{PropertyName}
로 가져온다. 즉, 속성을 멤버 변수로 두고, getter/setter로 접근하도록 한다는 것이다. (VB나 C#에서는 Property라는 좀 더 재미난 개념을 제공하지만 별로 OO스럽지 않아서 Java는 지원 안하나 보다. 하지만 개인적 취향은 PHP의 getter가 좋다.)
그럼 이 PropertyChangeEvent
를 Listening하면 해당 Component의 속성 변화를 알 수 있다는 것인데, 기껏해야 Visible, Enable 등의 변화를 알 수 있다는 것일까?
Java 진영에서는 JComponent(javax.swing.JComponent)
간의 데이터 교환을 위해서 javax.accessibility.AccessibleContext
를 제공하고, 이것은 각 JComponent
의 getAccessibleContext()
함수를 통해 얻어올 수 있다.
Java Swing에는 수많은 JComponent
가 존재하고, 각 JComponent
마다 관리하는 저마다의 값(Model)이 있다.
JTextField
이면 String type의 text를 관리할 것이고,JSlider
이면 int 값의 value를 관리할 것이다.
이러한 정보들이 각 JComponent
마다 천차만별이고 (심지어 Tree나 Table은 어찌할 것인가!) 이쯤 되니까 각각의 reference로 접근해서 해당 데이터를 접근하는 것은 쉬워도, 이들을 공통적으로 접근하기는 애매해진 것이다. (사실 나는 그래서 모 코드에서 instanceof 로 모든 JComponent
객체의 type로 분기해서 처리해주는 굉장한 것도 봤다.)
그래서 각 JComponent
마다 자신의 Context
를 반환할 수 있는데, 그 중에서 Accessible
한 것들, 즉 관리하는 값(Model)에 대한 접근을 class로 묶어서 객체를 반환하도록 한 것이다.
이 객체로 데이터만 접근할 수 있는 것이라면 재미가 없다. 재밌는 것은 이 Context의 값이 변화하면 여기서도 PropertyChangeEvent
가 발생한다는 것이다.
즉, 이 Context 객체에 PropertyChangeListener
를 걸면 해당 Component의 값(Model)의 변화를 감지할 수 있고, 이것은 두 Component의 Data Binding을 쉽게 해줄 수 있는 요소가 된다.
- 예를 들어
JSlider
객체의AccessibleContext
를 가져와서 해당ProperyChangeEvent
를 Listening을 하여JTextField
객체의 값으로setText()
함수를 통해 지정해줄 수 있다는 것이다.
물론 그냥 JSlider
의 ChangeListener
를 걸어서 처리할 수도 있지만, 요는 추상화를 통한 다형성, 즉 모든 JComponent
에 대해 동일한 방법을 제공한다는 것이다. (어떤 것은 ActionListener
, 어떤 것은 ItemListener
, 어떤 것은 ChangeListener
, 등등이라면 각각 JComponent
에 대해서 할 경우에는 모두 다른 Listener를 걸어주어 객체간의 Data Binding을 위한 Event 구독 관계가 복잡하게 꼬일 것이다.)
정리하면 다음과 같다.
- 각
JComponent
는 자신의 데이터를 접근할 수 있는 Context class를 모든Component
에 대해 추상화해놓은AccessibleContext
를 구현하여 반환할 수 있고, - 이 객체의
PropertyChangeEvent
를 Listener하면 Data Binding을 수행할 수 있다.
요즘 같이 데이터가 많아진 시대에 데이터 중심의 코딩이 예전보다 중요해졌다. 고로 Data Binding 기술에 대한 이야기가 여기저기서 많이 나오고 있는데(너무 나와서 이제는 안 나오나;) Java에서는 이를 구현하기 위해 이와 같은 방법을 이용할 수도 있다는 것이다.
마무리
쓰다보니 뭔가 재밌어져서 이것저것 쓰다보니 당초에 쓰고 싶었던 D&D나 Dockable이 아래로 내려가 버렸다.
그리고 의외로 AccessibleContext
에 대해서 열변을 토해버렸는데, 그만큼 저 쪽에는 재밌는게 많다는 것이다.
지난 번에 잠깐 Qt를 보면서 SIGNAL
과 SLOT
, emit
등의 개념을 보면서 참 재밌게도 연결하는구나 싶었는데, AccessibleContext
도 나름 그런 방식으로 각자 다른 Data에 대해 객체로 추상화하고, 동일하게 접근할 수 있는 interface
를 제공해준다는 점에서 재밌다는 것이다. 물론 내공이 더 쌓이면 더 재미난 짓을 할 수 있겠지만 말이다.
혼자 예제도 만들고 하면서 하다보니 시간이 너무 걸려서, 일단 쓰는 걸 멈춘다.
추후 이어서 올릴 예정.
아래는 목차
- Java GUI with Model, Editor, Renderer
- Java Graphics
- Drag and Drop, Dockable or Floatable
- Java GUI with Reflection
- Look and Feel