[JavaFX] 선언적 사용자 인터페이스 : 학습 곡선 일지 2편
그 이후로 이 언어의 많은 중요한 부분이 개선되었습니다.
업데이트된 학습 곡선 일지에서는 컴파일러 기반 버전의 언어 사용법을 보여줍니다.
사용자 인터페이스를 정의하기 위해 10년 가까이 자바 프로그래밍 언어를 사용해온 저는
JavaFX 스크립트를 처음 사용해보고 두 가지 환경 사이의 큰 차이점을 금방 느낄 수 있었습니다.
프로그래머는 자바 언어에서 사용자 인터페이스(UI) 정의를 위해 절차적 언어를 사용하지만
JavaFX Script에서는 UI 정의에 선언적 문을 사용할 수 있습니다.
이는 커다란 차이이며 여기에 적응하는 데는 약간의 시간과 노력이 필요할 수 있습니다.
UI 생성을 위한 새로운 선언적 스타일에 대해 알아보고자
기존 애플리케이션 UI를 자바 언어 구현에서 JavaFX 스크립트로 이식하기로 했습니다.
썬 개발자 네트워크의 자바 언어 허브에 있는 Swingworker 기사에서 만든 이미지 뷰어 애플리케이션을 선택했습니다.
원래 애플리케이션은 Java SE 6에서 SwingWorker
클래스의 사용 방법을 보여주기 위한 것이었지만
UI 자체는 JavaFX 스크립트로의 간편한 이전을 제공할 만큼 충분히 단순해 보입니다.
기존 사용자 인터페이스
기존 애플리케이션에서는 사용자가 유명한 Flickr 웹 사이트에서 이미지를 검색, 나열 및 표시할 수 있도록 했습니다.
사용자는 검색어를 입력할 수 있으며 애플리케이션은 REST API를 사용하여 매칭되는 축소판 이미지 목록을 Flickr에 쿼리합니다.
사용자는 축소판 이미지를 하나 선택하여 크고 상세한 이미지를 가져올 수 있습니다.
그림 1은 검색 결과가 나온 기존 애플리케이션을 보여줍니다.
이 UI는 위에서부터 아래로 다음 구성요소로 이루어져 있습니다.
- 기본 프레임 창 컨테이너
- 검색 레이블 및 검색 텍스트 필드
- 검색 매칭 레이블 및 진행 표시줄
- 매칭되는 축소판 이미지 목록과 짧은 설명
- 선택 레이블 및 진행 표시줄
- 선택한 이미지를 보여주는 레이블
UI는 JFrame
, JLabel
, JProgressBar
, JScrollPane
및 JList
.와 같은 일반 Swing 구성요소로 이루어집니다.
JList
구성요소는 축소판 이미지와 제공되는 짧은 설명을 표시하기 위한 사용자 정의 렌더러를 갖지만 여전히 상대적으로 간단한 UI로서
JavaFX 스크립트의 선언적 UI 측면을 조사하는 데 도움이 될 것입니다.
전체 애플리케이션의 구현을 시도해 보겠지만 현재로는 기존 UI와 적당히 비슷한 정도로도 충분합니다.
작동하는 데모처럼 극적이진 않겠지만 그림 2에 나온 비활성 UI는 JavaFX 스크립트의 선언적 UI에 대한 초기 목표를 보여줍니다.
원래 UI는 NetBeans IDE 6.1과 Matisse GUI 편집기를 사용하여 구현했습니다.
Swingworker 기사에서 모든 원래 코드 및 생성된 UI 주변의 코드를 다운로드 받을 수 있습니다.
코드에서 이 UI를 생성하기 위해 NetBeans IDE가 GroupLayout
를 어떻게 사용했는지 볼 수 있습니다.
lblSearch = new javax.swing.JLabel();
txtSearch = new javax.swing.JTextField();
lblImageList = new javax.swing.JLabel();
scrollImageList = new javax.swing.JScrollPane();
listImages = new JList(listModel);
lblSelectedImage = new javax.swing.JLabel();
lblImage = new javax.swing.JLabel();
progressMatchedImages = new javax.swing.JProgressBar();
progressSelectedImage = new javax.swing.JProgressBar();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Image Search");
lblSearch.setText("Search");
lblImageList.setText("Matched Images");
// ...
// event listeners, models, and cell renderers removed for this example
//
lblSelectedImage.setText("Selected Image");
lblImage.setBorder(javax.swing.BorderFactory.createLineBorder(
new java.awt.Color(204, 204, 204)));
lblImage.setFocusable(false);
lblImage.setMaximumSize(new java.awt.Dimension(500, 500));
lblImage.setMinimumSize(new java.awt.Dimension(250, 250));
lblImage.setOpaque(true);
lblImage.setPreferredSize(new java.awt.Dimension(500, 250));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lblImage, javax.swing.GroupLayout.Alignment.LEADING,
javax.swing.GroupLayout.DEFAULT_SIZE, 462, Short.MAX_VALUE)
.addComponent(scrollImageList, javax.swing.GroupLayout.DEFAULT_SIZE,
462, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lblImageList)
.addComponent(lblSelectedImage))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(progressMatchedImages, javax.swing.GroupLayout.DEFAULT_SIZE,
350, Short.MAX_VALUE)
.addComponent(progressSelectedImage, javax.swing.GroupLayout.DEFAULT_SIZE,
350, Short.MAX_VALUE)))
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addComponent(lblSearch)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtSearch, javax.swing.GroupLayout.DEFAULT_SIZE,
411, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lblSearch)
.addComponent(txtSearch, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lblImageList)
.addComponent(progressMatchedImages, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(scrollImageList, javax.swing.GroupLayout.PREFERRED_SIZE, 235,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lblSelectedImage)
.addComponent(progressSelectedImage, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblImage, javax.swing.GroupLayout.DEFAULT_SIZE, 305, Short.MAX_VALUE)
.addContainerGap())
);
pack();
}
javax.swing.GroupLayout
을 사용하여 UI 코드를 생성했습니다. JavaFX 스크립트용의 설계 도구가 개발 중이지만 아직은 사용할 수 없습니다.
이 인터페이스에 필요한 UI 구성요소는 javafx.ext.swing
패키지 및 javafx.scene
으로 시작하는
다양한 패키지에 들어 있습니다. UI 구축은 계층 구조적인 Swing 기반 접근 방법을 따릅니다.
이 시리즈의 1편에서 언급한 것처럼 앞으로 JavaFX 개발자는 노드 기반 접근 방법을 사용할 것입니다.
노드 기반 접근 방법으로 UI를 구축하면 javafx.application
클래스의 일부 클래스가 필요할 것입니다.
JavaFX 스크립트는 패키지 구조 및 가져오기 문을 지원한다는 점에서 자바 언어와 유사합니다.
구성요소에 대해 공부하는 동안은 전체 패키지를 가져오는 대신 한 번에 하나씩 특정 구성요소를 가져오겠습니다.
이는 매우 지루하지만 하나씩 사용하게 함으로써 패키지에 어떤 구성요소가 있는지 보여줍니다.
JavaFX 스크립트 구성요소는 height
, width
, text
및 content
와 같은 속성을 갖습니다.
content
속성은 자녀 구성요소를 갖습니다. 구성요소에 따라 content
속성은 array로 선언되는
여러 개의 구성요소를 포함할 수 있습니다. 이미지 검색 애플리케이션을 위해 사용자 인터페이스를 선언할 때는
JavaFX 스크립트 구성요소의 선택 및 사용 후 속성을 설정합니다.
예를 들어 다음의 짧은 스크립트는 빈 프레임을 정의합니다. 이 코드는 컨텐츠가 없는 프레임 컨테이너를 생성합니다.
import javafx.ext.swing.SwingFrame;
SwingFrame {
title: "JFX Image Search"
height: 500
width: 500
visible: true
}
Swing의 JFrame
에 해당하는 JavaFX 스크립트는 javafx.ext.swing.SwingFrame
입니다.
title
속성은 프레임에 나타나는 텍스트인 창 프레임 제목을 선언합니다.
height
및 width
속성은 픽셀 단위로 크기 규격을 정의합니다.
마지막으로 visible
속성은 Swing의 JFrame
클래스 setVisible
메소드와 유사하게 프레임의 가시성 여부를 선언합니다.
javafx.ext.swing
패키지에는 SwingFrame
, Label
, BorderPanel
, FlowPanel
및 List
와 같은 구성요소가 포함됩니다.
이러한 이름은 일반적인 Swing 구성요소 같으므로 대상 UI 구현에 이들을 먼저 사용해보겠습니다.
javafx.scene
패키지의 HorizontalAlignment
구성요소와 javafx.scene.paint
패키지의 Color
구성요소도 사용했습니다.
그러나 JavaFX 스크립트 패키지에 ProgressBar
구성요소는 아직 없습니다.
따라서 진행 표시줄을 생성하는 함수를 코딩했습니다. 이 시리즈의 3편에서는 JavaFX 스크립트 함수를 검토합니다.
BorderPanel
및 FlowPanel
등의 구성요소와 진행 표시줄의 함수를 사용하기로 결정하고 다음
JavaFXImageSearchUI1.fx
코드를 생성했습니다.
package com.sun.demo.jfx;
import javafx.ext.swing.SwingFrame;
import javafx.ext.swing.BorderPanel;
import javafx.ext.swing.FlowPanel;
import javafx.ext.swing.Label;
import javafx.ext.swing.TextField;
import javafx.ext.swing.List;
import javafx.ext.swing.Component;
import javafx.scene.paint.Color;
import javafx.scene.HorizontalAlignment;
import java.awt.Dimension;
function createProgressBar(preferredSize:Integer[]) :Component {
var jprogressbar = new javax.swing.JProgressBar();
var comp = Component.fromJComponent(jprogressbar);
comp.preferredSize = preferredSize;
comp;
}
SwingFrame {
title: "JFX Image Search"
content: BorderPanel {
top: FlowPanel {
alignment: HorizontalAlignment.LEFT
content: [
Label {
text: "Search"
},
TextField {
columns: 50
}
]
}
center: BorderPanel {
top: FlowPanel {
alignment: HorizontalAlignment.LEFT
content: [
Label {
text: "Matched Images"
},
createProgressBar([360, 20])
]
}
center: List {
preferredSize: [100, 200]
}
bottom: FlowPanel {
alignment: HorizontalAlignment.LEFT
content: [
Label {
text: "Selected Image"
},
createProgressBar([365, 20])
]
}
}
bottom: Label {
preferredSize: [400, 300]
}
}
visible: true
}
NetBeans IDE 6.1에 프로젝트를 생성한 후에 JFXImageSearchUI1.fx
파일에 위의 코드를 입력했습니다.
미리보기 버튼을 클릭하면 그림 3과 같은 결과가 나타납니다.
그림 3. JavaFX 이미지 검색 UI -- 첫 번째 시도
'개발 이야기 > Java' 카테고리의 다른 글
[JavaFX] 웹 서비스 액세스 :학습 곡선 일지 4편 (0) | 2008.09.25 |
---|---|
[JavaFX] JavaFX 스크립트 함수 : 학습 곡선 일지 3편 (0) | 2008.09.25 |
[JavaFX] 스크립트 탐구 : 학습 곡선 일지 1편 (0) | 2008.09.25 |
SAML을 이용한 SSO Service의 구현 (0) | 2008.04.03 |
SVN 서버 설치 및 거북이(Tortoise) SVN 설치하기 (0) | 2008.03.13 |