2007 년 8월과 9월에 썬 개발자 네트워크의 John O'Conner는 JavaFX 스크립트 프로그래밍 언어(이 기사에서는 JavaFX 스크립트라고 줄여서 부름)를 시작하는 사용자에게 도움을 주고자 "학습 곡선 일지(Learning Curve Journal)"라는 제목의 시리즈를 기고했습니다.

그 이후로 이 언어의 많은 중요한 부분이 개선되었습니다. 아마도 가장 중요한 변화는 JavaFX 스크립트의 초기 인터프리터 기반 버전을 대신하여 컴파일러 기반 버전을 사용할 수 있게 되었다는 점입니다. 이전의 학습 곡선 일지에서는 인터프리터 기반 버전 사용에 대해 설명했습니다.

업데이트된 학습 곡선 일지에서는 컴파일러 기반 버전의 언어 사용법을 보여줍니다. 최신 내용을 반영하여 다른 변경 사항도 적용되었습니다.

사용자 인터페이스를 정의하기 위해 10년 가까이 자바 프로그래밍 언어를 사용해온 저는 JavaFX 스크립트를 처음 사용해보고 두 가지 환경 사이의 큰 차이점을 금방 느낄 수 있었습니다. 프로그래머는 자바 언어에서 사용자 인터페이스(UI) 정의를 위해 절차적 언어를 사용하지만 JavaFX Script에서는 UI 정의에 선언적 문을 사용할 수 있습니다. 이는 커다란 차이이며 여기에 적응하는 데는 약간의 시간과 노력이 필요할 수 있습니다.

UI 생성을 위한 새로운 선언적 스타일에 대해 알아보고자 기존 애플리케이션 UI를 자바 언어 구현에서 JavaFX 스크립트로 이식하기로 했습니다. 썬 개발자 네트워크자바 언어 허브에 있는 Swingworker 기사에서 만든 이미지 뷰어 애플리케이션을 선택했습니다. 원래 애플리케이션은 Java SE 6에서 SwingWorker 클래스의 사용 방법을 보여주기 위한 것이었지만 UI 자체는 JavaFX 스크립트로의 간편한 이전을 제공할 만큼 충분히 단순해 보입니다.


기존 사용자 인터페이스

기 존 애플리케이션에서는 사용자가 유명한 Flickr 웹 사이트에서 이미지를 검색, 나열 및 표시할 수 있도록 했습니다. 사용자는 검색어를 입력할 수 있으며 애플리케이션은 REST API를 사용하여 매칭되는 축소판 이미지 목록을 Flickr에 쿼리합니다. 사용자는 축소판 이미지를 하나 선택하여 크고 상세한 이미지를 가져올 수 있습니다. 그림 1은 검색 결과가 나온 기존 애플리케이션을 보여줍니다.

그림 1. 검색 결과가 나온 애플리케이션 UI

이 UI는 위에서부터 아래로 다음 구성요소로 이루어져 있습니다.

  • 기본 프레임 창 컨테이너
     
  • 검색 레이블 및 검색 텍스트 필드
     
  • 검색 매칭 레이블 및 진행 표시줄
     
  • 매칭되는 축소판 이미지 목록과 짧은 설명
     
  • 선택 레이블 및 진행 표시줄
     
  • 선택한 이미지를 보여주는 레이블
     

UI는 JFrame, JLabel, JProgressBar, JScrollPaneJList.와 같은 일반 Swing 구성요소로 이루어집니다. JList 구성요소는 축소판 이미지와 제공되는 짧은 설명을 표시하기 위한 사용자 정의 렌더러를 갖지만 여전히 상대적으로 간단한 UI로서 JavaFX 스크립트의 선언적 UI 측면을 조사하는 데 도움이 될 것입니다. 전체 애플리케이션의 구현을 시도해 보겠지만 현재로는 기존 UI와 적당히 비슷한 정도로도 충분합니다. 작동하는 데모처럼 극적이진 않겠지만 그림 2에 나온 비활성 UI는 JavaFX 스크립트의 선언적 UI에 대한 초기 목표를 보여줍니다.

그림 2. 애플리케이션 UI

원래 UI는 NetBeans IDE 6.1과 Matisse GUI 편집기를 사용하여 구현했습니다. Swingworker 기사에서 모든 원래 코드 및 생성된 UI 주변의 코드를 다운로드 받을 수 있습니다. 코드에서 이 UI를 생성하기 위해 NetBeans IDE가 GroupLayout를 어떻게 사용했는지 볼 수 있습니다.

    private void initComponents() {
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();
}
 
NetBeans IDE를 사용한 UI 레이아웃은 쉽습니다. 드래그 앤 드롭이면 충분합니다. 이 예제에서 NetBeans IDE 6.1은 여러 가지 호스트 플랫폼에서 정확한 크기, 위치 및 구성요소의 간격을 제공하는 javax.swing.GroupLayout을 사용하여 UI 코드를 생성했습니다. 결과 코드는 그다지 읽기 쉽진 않지만 도구 지원이 뛰어나 레이아웃 코드를 직접 수동으로 작업할 필요가 없습니다.


선언적 JavaFX 스크립트 인터페이스

JavaFX 스크립트용의 설계 도구가 개발 중이지만 아직은 사용할 수 없습니다. 그러나 JavaFX 스크립트 플러그인이 포함된 NetBeans IDE 6.1의 미리보기 기능을 사용하면 수동으로 UI 코드를 입력하고 결과를 즉시 볼 수 있습니다.

이 인터페이스에 필요한 UI 구성요소는 javafx.ext.swing 패키지 및 javafx.scene으로 시작하는 다양한 패키지에 들어 있습니다. UI 구축은 계층 구조적인 Swing 기반 접근 방법을 따릅니다. 이 시리즈의 1편에서 언급한 것처럼 앞으로 JavaFX 개발자는 노드 기반 접근 방법을 사용할 것입니다. 노드 기반 접근 방법으로 UI를 구축하면 javafx.application 클래스의 일부 클래스가 필요할 것입니다.

JavaFX 스크립트는 패키지 구조 및 가져오기 문을 지원한다는 점에서 자바 언어와 유사합니다. 구성요소에 대해 공부하는 동안은 전체 패키지를 가져오는 대신 한 번에 하나씩 특정 구성요소를 가져오겠습니다. 이는 매우 지루하지만 하나씩 사용하게 함으로써 패키지에 어떤 구성요소가 있는지 보여줍니다.

JavaFX 스크립트 구성요소는 height, width, textcontent와 같은 속성을 갖습니다. 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 속성은 프레임에 나타나는 텍스트인 창 프레임 제목을 선언합니다. heightwidth 속성은 픽셀 단위로 크기 규격을 정의합니다. 마지막으로 visible 속성은 Swing의 JFrame 클래스 setVisible 메소드와 유사하게 프레임의 가시성 여부를 선언합니다.


Border Panel 및 Flow Panel

javafx.ext.swing 패키지에는 SwingFrame, Label, BorderPanel, FlowPanelList와 같은 구성요소가 포함됩니다. 이러한 이름은 일반적인 Swing 구성요소 같으므로 대상 UI 구현에 이들을 먼저 사용해보겠습니다. javafx.scene 패키지의 HorizontalAlignment 구성요소와 javafx.scene.paint 패키지의 Color 구성요소도 사용했습니다. 그러나 JavaFX 스크립트 패키지에 ProgressBar 구성요소는 아직 없습니다. 따라서 진행 표시줄을 생성하는 함수를 코딩했습니다. 이 시리즈의 3편에서는 JavaFX 스크립트 함수를 검토합니다. BorderPanelFlowPanel 등의 구성요소와 진행 표시줄의 함수를 사용하기로 결정하고 다음 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 -- 첫 번째 시도


프레임의 컨텐츠는 BorderPanel 구성요소로, top 속성은 FlowPanel 구성요소에 지정되고 center 속성은 다른 BorderPanel 구성요소에 지정되며 bottom 속성은 Label 구성요소에 지정되었습니다. FlowPanel 구성요소는 content 등록 정보를 갖습니다. 이 등록 정보에 하나 이상의 구성요소를 넣을 수 있습니다. 여러 개의 구성요소를 삽입할 때는 array를 정의하는 괄호 속에 구성요소를 넣어야 합니다. 다음과 같이 쉼표를 사용하여 컨텐츠 array에서 개별 구성요소를 구분합니다.

  content: [
  Label {
     text: "Search"
     },
  TextField {
     columns: 50
  }
  ]
 

상단 FlowPanel에는 Label 구성요소와 TextField 구성요소가 중첩되어 있습니다. FlowPanel 의 방향 속성은 HorizontalAlignment.LEFT입니다. 이는 FlowPanel 내의 구성요소를 가로와 왼쪽으로 정렬합니다. 가운데의 BorderPanel은 이 영역의 상단에 가로로 정렬된 레이블과 진행 표시줄을, 중앙에 목록을, 하단에 다른 레이블과 진행 표시줄을 레이아웃합니다. 프레임의 하단에는 레이블이 들어갑니다.

FlowPanel 구성요소는 레이블-구성요소 쌍을 만들기에 좋습니다. 여기에서는 여러 개의 FlowPanel을 사용하여 레이블과 TextField 등의 구성요소를 묶었습니다.

createProgressBar 함수는 JavaFX 스크립트를 사용하여 Swing 구성요소로부터 javafx.gui 구성요소를 생성하는 방법을 보여줍니다. 여기에서는 Swing JProgressBar 구성요소로부터 JavaFX 스트립트 진행 표시줄을 생성했습니다.


 
 function createProgressBar(preferredSize:Integer[]) :Component {
          var jprogressbar = new javax.swing.JProgressBar();
          var comp = Component.fromJComponent(jprogressbar);
          comp.preferredSize = preferredSize;
          comp;
  }
 

첫 번째 시도에서 UI는 상당히 잘 작동하지만 원래 UI를 제대로 재현하지는 못합니다. 그래서 다른 접근 방법을 시도해 보겠습니다.


클러스터

원래 UI를 복제하려는 두 번째 시도에서는 JavaFX 스크립트 ClusterPanel 구성요소를 활용하겠습니다. 이 구성요소를 사용하여 프레임 내에서 구성요소를 클러스터할 수 있습니다. JavaFX 스크립트에는 ClusterPanel 내에서 구성요소를 순서대로나 병렬로 정리하기 위해 사용할 수 있는 SequentialClusterParallelCluster도 포함됩니다.

진행 표시줄, 검색 텍스트 필드, 축소판 이미지 목록 및 선택된 이미지 표시 영역의 최대 너비 및 높이를 설정하기 위해 JavaFX 스크립트 Layout 구성요소도 활용하기로 결정했습니다. Layout 구성요소는 UNLIMITED_SIZE 등의 레이아웃 관련 상수를 제공합니다.

UI의 두 번째 버전은 다음 JFXImageSearchUI.fx 파일에 코딩되었습니다.


   package com.sun.demo.jfx;

import javafx.ext.swing.Component;
import javafx.ext.swing.SwingFrame;
import javafx.ext.swing.Label;
import javafx.ext.swing.TextField;
import javafx.ext.swing.List;
import javafx.ext.swing.Cluster;
import javafx.ext.swing.Layout;
import javafx.ext.swing.ClusterPanel;
import javafx.ext.swing.SequentialCluster;
import javafx.ext.swing.ParallelCluster;
import javafx.scene.paint.Color;
import java.awt.Dimension;
import java.lang.System;

import javax.swing.border.LineBorder;

function createProgressBar() :Component {
var jprogressbar = new javax.swing.JProgressBar();
var comp = Component.fromJComponent(jprogressbar);
comp.hmax = Layout.UNLIMITED_SIZE;
comp;
}

var searchLabel = Label {
text: "Search:"

}
var searchTextField = TextField {
hmax: Layout.UNLIMITED_SIZE
columns: 50
}

var matchedImageLabel = Label {
text: "Matched Images"
}
var matchedImagePB = createProgressBar();


var thumbnailList = List {
preferredSize:[300, 230]
hmax: Layout.UNLIMITED_SIZE
vmax: Layout.UNLIMITED_SIZE
}

var selectedImageLabel = Label {
text: "Selected Image"
}
var selectedImagePB = createProgressBar();

var selectedImageDisplay = Label {
preferredSize: [400,300]
hmax: Layout.UNLIMITED_SIZE
vmax: Layout.UNLIMITED_SIZE
}

selectedImageDisplay.getJComponent().setOpaque(true);
selectedImageDisplay.getJComponent().setBackground(Color.WHITE.getAWTColor());
selectedImageDisplay.getJComponent().setBorder(new LineBorder(Color.BLACK.getAWTColor(), 1, true));

SwingFrame {
title: "JavaFX Image Search"
content:
// main panel within the frame
ClusterPanel {

hcluster: SequentialCluster {
content: [
ParallelCluster{ // mainCol
resizable: true
content: [
SequentialCluster{
content: [
ParallelCluster { //searchLabelCol
content: searchLabel
},
ParallelCluster { //searchTextFieldCol
resizable: true
content: searchTextField
}
]},
SequentialCluster {
content: [
ParallelCluster { //lblCol
content: matchedImageLabel
},
ParallelCluster { //progressBarCol
resizable: true
content: matchedImagePB
}
]},
thumbnailList,
SequentialCluster {
content: [
ParallelCluster { //lblCol
content: selectedImageLabel
},
ParallelCluster { //progressBarCol
resizable: true
content: selectedImagePB
}
]},
selectedImageDisplay
]
}
]

}

vcluster: SequentialCluster {
content: [
ParallelCluster{ //searchRow
content: SequentialCluster {
content: [
ParallelCluster { // row
content: [searchLabel, searchTextField]
}
]
}
},
ParallelCluster{ // matchedProgressRow
content: SequentialCluster {
content: [
ParallelCluster { // row
content: [matchedImageLabel, matchedImagePB]
}
]
}
},
ParallelCluster{ // thumbNailRow
resizable:true
content: thumbnailList
},
ParallelCluster{ // selectedProgressRow
content: SequentialCluster {
content: [
ParallelCluster { // row
content: [selectedImageLabel, selectedImagePB]
}
]
}
},
ParallelCluster{ // imageRow
content: selectedImageDisplay
}

]
}

}
visible: true
}


 
이 코드의 결과는 훨씬 보기 좋습니다.
그림 4에 나온 것처럼 구성요소는 이제 프레임의 왼쪽과 오른쪽으로 완벽하게 정렬되었습니다.

그림 4. JavaFX 이미지 검색 UI -- 두 번째 시도

프레임의 주 컨텐츠는 ClusterPanel 구성요소입니다. ClusterPanel 내에서 SequentialCluster 구성요소 그룹의 구성요소는 모두 순서대로 있습니다. ParallelCluster 구성요소 그룹의 구성요소는 병렬로 있습니다. 예를 들어 다음은 구성요소를 프레임의 상단 영역에 정리합니다. 검색 레이블과 검색 텍스트 필드를 병렬 열로 클러스터합니다.

 
 hcluster: SequentialCluster {
      content: [
          ParallelCluster{ // mainCol
              resizable: true
              content: [
                  SequentialCluster{
                      content: [
                          ParallelCluster {  //searchLabelCol
                              content: searchLabel
                          },
                          ParallelCluster { //searchTextFieldCol
                              resizable: true
                              content: searchTextField
                          }
              ]},
 

SequentialClusterSequentialClusterParallelCluster와 같은 ClusterElement 구성요소를 지정할 수 있는 content 속성을 갖습니다. ParallelCluster 구성요소는 크기 조정이 가능하므로 필요에 따라 프레임 너비를 채웁니다.

인터페이스의 나머지도 같은 패턴을 따릅니다. 인터페이스의 각 5개 영역 내에서 구성요소는 SequentialCluster 구성요소 내에서 ParallelCluster 구성요소를 사용하여 그룹화 및 정리됩니다. 예를 들어 다음 코드는 매칭되는 이미지 레이블과 매칭되는 이미지 진행 표시줄을 병렬 열로 클러스터합니다. 클러스터 다음에는 검색과 매칭되는 이미지의 축소판 목록이 나옵니다.


 
SequentialCluster {
      content: [
          ParallelCluster {  //lblCol
              content: matchedImageLabel
          },
          ParallelCluster { //progressBarCol
              resizable: true
              content: matchedImagePB
          }
      ]},
  thumbnailList,
 

한 가지 주목할 점은 javafx.ext.swing 패키지의 Label 구성요소가 테두리 특성, 불투명도 또는 배경 색상의 설정을 위한 속성을 현재 지원하지 않는다는 것입니다. 따라서 이들 설정을 위해 getJComponent 함수에 대한 Label 구성요소의 지원을 활용했습니다. 함수는 JavaFX 스크립트 구성요소로 캡슐화된 Swing jComponent를 반환합니다. 이 경우에는 Swing Label 구성요소를 반환합니다. 다음 코드를 사용하여 필요한 레이블 속성을 정의할 수 있습니다.


var selectedImageDisplay = Label {
         ...
  }

  selectedImageDisplay.getJComponent().setOpaque(true);
  selectedImageDisplay.getJComponent().setBackground(Color.WHITE.getAWTColor());
  selectedImageDisplay.getJComponent().setBorder(new LineBorder(Color.BLACK.getAWTColor(), 1, true));
 

설계 도구 없이 이미지 검색 사용자 인터페이스를 복제하는 것에 대해 처음에 걱정했던 것과 달리 가장 유용하고 필요한 기능은 빠르고 쉽게 액세스가 가능합니다. 또한 NetBeans IDE용 JavaFX 플러그인은 구성요소를 사용하여 작업할 때 사용 가능한 속성을 띄우는 문맥 인식 코드 완성을 제공합니다. 코드 완성을 사용하면 UI의 구현이 처음 생각처럼 어렵지만은 않습니다. 그림 5는 IDE에서 Ctrl+spacebar를 입력할 때 활성화되는 일부 팝업 옵션을 보여줍니다.


그림 5. 옵션


요약

UI 생성을 위한 선언적 구문을 살펴보기 위해 기존 애플리케이션의 UI를 이식했습니다. 원래 애플리케이션에서는 구성요소의 배치 및 정렬에 Swing의 GroupLayout을 사용했습니다. NetBeans IDE 6.1은 아직 JavaFX 스크립트를 위한 그래픽 설계 도구를 지원하지 않지만 수동으로 인터페이스를 레이아웃하는 것은 처음 걱정했던것 만큼 어렵지 않았습니다. ClusterPanel, SequentialClusterParallelCluster 구성요소의 조합을 통해 JavaFX 스크립트 구현은 원래 UI와 사실상 똑같아 보입니다. 이들 조합에 NetBeans IDE용 JavaFX 플러그인 및 문맥 인식 코드 완성이 더해져 작업은 더욱 쉬워졌습니다.


자세한 정보


이 글의 영문 원본은
# Learning Curve Journal, Part 2: Declarative User Interfaces
에서 보실 수 있습니다.
블로그 이미지

맨오브파워

한계를 뛰어 넘어서..........

,