목공책 하나 들이셔요~

2014년 4월 4일 금요일

[JavaFX]따라하기#4 - FXML로 UI만들기

이 글은 Getting Started with JavaFX 시리즈 중에서 "Using FXML to Create a User Interface"를 번역한 것입니다. 원문은 다음 링크를 확인하세요.
http://docs.oracle.com/javafx/2/get_started/fxml_tutorial.htm


이 튜토리얼은 FXML을 쓰면 어떤 잇점이 있는지를 보여 드릴겁니다. FXML은 유저 인터페이스 구조를 XML로 정의한 것인데 이를 통해 로직 코드와 UI를 분리할 수 있습니다. 즉 FXML과 CSS는 디자이너에 의해서 편집하고 Java코드는 프로그래머에 의해서 개발하는 식의 분업을 가능하게 합니다.

이 튜토리얼 시리즈를 처음부터 보아 왔다면 Login 어플리케이션을 어떻게 만드는지 아실 겁니다. 여기서는 FXML을 이용하여 이전 과정에 만든 유저 인터페이스와 똑 같은 모양의 어플리케이션을 만들 것입니다. 결과적으로 아래와 같은 모양이 될 겁니다.


프로젝트 만들기

먼저 NetBeans IDE에서 JavaFX FXML 프로젝트를 만들어야 합니다. 다음과 같이 하세요.

1. File 메뉴에서 New Project를 선택합니다.

2. JavaFX 어플리케이션 카테고리에서 JavaFX FXML Application을 선택하고, Next를 클릭합니다.

3. 프로젝트 이름을 FXMLExample로 하고 Finish를 클릭합니다. NetBeans IDE는 기본적인 Hello World 어플리케이션을 보여줄 겁니다. 다음과 같은 파일로 구성됩니다.
  • FXMLExample.java : 이 파일은 FXML 어플리케이션에서 필요한 Java 코드를 담습니다.
  • Sample.fxml : 이 파일은 유저 인터페이스를 정의하는 FXML 소스 코드를 담습니다.
  • SampleController.java : 이 파일은 마우스와 키보드 입력을 다루는 컨트롤러 소스를 담습니다.

4. SampleController.java 파일을 FXMLExampleController.java로 이름을 변경합니다. 그래야 제짝인 것 같지요.
  • Project 윈도우에서 SampleController.java 파일에 오른쪽 클릭을 합니다. 그리고 Refactor를 선택하고 Rename을 선택합니다. 
  • FXMLExampleController라고 이름을 입력하고 Refactor 버튼을 클릭합니다.

5. Sample.fxml을 fxml_example.fxml로 이름을 변경합니다.
  • Sample.fxml에 오른쪽 버튼을 클릭하고 Rename을 선택합니다.
  • fxml_example이라고 입력하고 OK를 누릅니다.

FXML 소스 파일을 읽어들이기

먼저 수정해야 할 파일은 FXMLExample.java 파일입니다. 이 파일은 어플리케이션 메인 클래스를 설정하고 Stage와 Scene을 정의합니다. 그리고 FXMLLoader class를 이용하여 FXML 소스 파일을 읽어들여서 결과적으로 Object Graph를 리턴합니다.

원래 제공된 소스에서 굵게 표시된 부분을 아래와 같이 수정합니다.

    @Override
    public void start(Stage stage) throws Exception {
       Parent root = FXMLLoader.load(getClass().getResource("fxml_example.fxml"));
    
        Scene scene = new Scene(root, 300, 275);
    
        stage.setTitle("FXML Welcome");
        stage.setScene(scene);
        stage.show();
    }

Scene을 생성할 때는 폭과 너비를 지정하는 것이 좋습니다. 위의 예에서는 300 x 275를 선택했습니다. 만일 크기를 지정하지 않으면 컨텐츠를 표시할 수 있는 최소 크기로 정해지기 때문에 의도하지 않게 매우 작은 윈도우가 생성될 수도 있습니다.

Import 구문을 수정하기

다음으로 fxml_example.fxml 파일을 수정합니다. 이 파일은 어플리케이션이 시작할 때 보일 유저 인터페이스를 정의합니다. 먼저 할 일은 아래와 같이 import 구문들을 추가하는 것입니다.

<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

Java와 같이 class의 이름에 패키지 이름까지 포함된 완전 경로 (Fully qualified)를 사용하면 import 구문이 필요 없습니다. 하지만 class명 만을 사용하려면 위와 같이 import 구문을 이용하여 어느 패키지에서 클래스를 읽어올 것인지를 지정해 주어야 합니다.

GridPane 레이아웃 만들기

NetBeans가 자동으로 만들어 주는 소스 코드는 AnchorPane을 사용합니다. Login 폼을 위해서는 여러 컨트롤들이 그리드 형태로 배치되어야 하므로 GridPane이 편리합니다. 그러므로 AnchorPane과 그 자손들을 제거하고 아래와 같은 GridPane을 정의하여 넣습니다.

<GridPane fx:controller="fxmlexample.FXMLExampleController" xmlns:fx="http://javafx.com/fxml"                   alignment="center" hgap="10" vgap="10">
    <padding>
        <Insets top="25" right="25" bottom="10" left="25"/>
    </padding>
</GridPane>

이 어플리케이션에서 루트 엘리먼트는 바로 이 GridPane입니다. GridPane에는 여러가지 속성들이 정의되어 있는데 fx:controller는 마크업에서 컨트롤러 기반의 이벤트 핸들러를 사용할 경우 명시해야 합니다. xmlns:fx는 fx 네임 스페이스를 정의합니다.

align="center"는 GridPane이 Scene에서 중앙에 위치하도록 정렬함을 의미하고, hgap과 vgap은 각 그리드 셀간의 수평/수직 간격을 지정합니다. 그 아래의 padding은 GridPane의 바깥쪽 간격을 지정합니다.

윈도우의 크기를 변경하면 GridPane내의 노드들도 레이아웃 제한(layout constraint)에 의해서 크기가 조정되고 위치가 옮겨집니다. 이 예에서는 GridPane은 윈도우 크기와 관계없이 항상 중앙에 위치하게 되며, padding 속성은 윈도우가 작아지더라도 보장되는 바깥쪽 간격을 지정하게 됩니다.

텍스트와 암호 필드 추가하기

위의 완성된 Login 폼을 보면 "Welcome"이라는 타이틀과 정보를 입력받는 ID와 암호 입력창을 볼 수 있습니다. 이를 위해 GridPane 태그 안쪽에 아래와 같은 내용이 들어가야 합니다.

    <Text text="Welcome" 
        GridPane.columnIndex="0" GridPane.rowIndex="0"
        GridPane.columnSpan="2"/>
 
    <Label text="User Name:"
        GridPane.columnIndex="0" GridPane.rowIndex="1"/>
 
    <TextField 
        GridPane.columnIndex="1" GridPane.rowIndex="1"/>
 
    <Label text="Password:"
        GridPane.columnIndex="0" GridPane.rowIndex="2"/>
 
    <PasswordField fx:id="passwordField" 
        GridPane.columnIndex="1" GridPane.rowIndex="2"/>

첫번째 줄은 Text 객체를 생성하고 텍스트 값을 "Welcome"으로 설정합니다. GridPane.columnIndex와 GridPane.rowIndex 속성은 Text 컨트롤이 그리드의 어디에 위치할 것인지를 지정합니다. 이 속성들의 숫자는 0에서부터 시작합니다. 그러므로 Text 컨트롤이 지정된 (0, 0) 좌표는 첫번째 행과 첫번째 열의 위치를 의미합니다. GridPane.columnSpan 속성은 2로 설정되었는데 이는 Welcome이 출력된 제목이 길기 때문에 두 개의 열을 병합하라는 것입니다. 나중에 스타일 시트를 적용하여 폰트 크기를 증가시킬 것이므로 여유가 필요합니다.

다음 줄은 "User Name"이라는 텍스트가 담긴 Label 객체를 생성합니다. 위치는 0번째 열과 1번째 행입니다. TextField 객체는 바로 오른쪽인 1번째 열과 1번째 행에 위치합니다. 다른 Lable과 PasswordField 객체는 그 아래에 위치하는데 비슷하게 열과 행의 위치를 지정합니다.

그리드 레이아웃을 사용할 때는 그리드 라인을 보이할 수 있습니다. 이것은 디버깅할 때 매우 유용합니다. FXML에서는 gridLinesVisible 속성을 true로 바꾸어 주면 그리드 라인이 보이게 됩니다. 앞에서 언급했던 <padding>...</padding> 태그 아래에 <gridLinesVisible>true</gridLinesVisible>이라는 태그를 넣어주면 됩니다. 그러면 아래 그림과 같이 그리드의 각 셀과 패딩을 식별할 수 있게 됩니다.


버튼과 텍스트를 추가하기

마지막으로 필요한 두개의 컨트롤은 서브밋을 할 Button과 메시지를 출력할 Text 컨트롤입니다. 아래의 XML 구문을 </GridPane> 이전에 추가하면 됩니다.

<HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="1" GridPane.rowIndex="4">
    <Button text="Sign In" onAction="#handleSubmitButtonAction"/>
</HBox>
<Text fx:id="actiontarget" GridPane.columnIndex="1" GridPane.rowIndex="6"/>

HBox는 GridPane 레이아웃에 적용된 디폴트 정렬 방식과는 다른 정렬 방식이 필요해서 추가한 것입니다. HBox의 정렬 방식은 bottom_right로 설정되었는데 이는 서브밋 버튼이 오른쪽 아래로 정렬되게 하기 위한 것입니다. HBox는 그리드의 열 1과 행 4에 추가되었습니다.

HBox는 하나의 자식 컨트롤 Button을 가지고 있습니다. 텍스트는 "Sign In"으로 셋팅되었는데 onAction 속성에 handleSubmitButtonAction() 메쏘드가 할당되었습니다. FXML은 어플리케이션의 컨트롤을 배치하는 것은 잘 표현하지만 어플리케이션이 어떻게 동작하는지는 표현하지 못합니다. 그러므로 handleSubmitButtonAction() 메쏘드는 Java 코드로 구현해야 합니다.

Text 컨트롤에 fx:id 속성을 부여한 것은 이 컨트롤의 이름을 네임 스페이스에 등록하기 위한 것입니다. 이렇게 fx:id를 부여해 두어야 Java코드에서 이 컨트롤을 직접 지정할 수 있습니다. 혹은 컨트롤러 필드를 이용하여 컨트롤러와 마크업이 어떻게 연관되는지를 명확하게 표현할 수 있습니다..

이벤트 핸들러 달기

이제 사용자가 버튼을 누르면 텍스트의 내용을 변경하는 코드를 추가해 봅니다. 이 코드는 FXMLExampleController.java 파일에 정의할 수 있습니다. NetBeans IDE가 생성한 기본 코드들은 모두 삭제하고 다음의 코드를 입력합니다.

package fxmlexample;
 
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.text.Text;
 
public class FXMLExampleController {
    @FXML private Text actiontarget;
    
    @FXML protected void handleSubmitButtonAction(ActionEvent event) {
        actiontarget.setText("Sign in button pressed");
    }
}

@FXML 어노테이션은 FXML 마크업에서 사용되는 비공개 컨트롤러 멤버와 메쏘드를 정의하는데 쓰입니다. handleSubmitButtonAction 메쏘드는 actionTarget 변수에 할당된 Text 컨트롤의 텍스트를 변경하도록 구현되었습니다.


이제 당신은 어플리케이션을 실행할 수 있습니다. 그러면 아래와 같은 대화상자를 볼 수 있을 겁니다.


이벤트 핸들러를 스크립트로 작성하기

Java코드를 작성하는 대신에 이벤트 핸들러를 JSR-223과 호환되는 스크립트로 작성할 수도 있습니다. 가능한 스크립트 언어는 JavaScript, Groovy, Jython, Clojure 등이 있습니다.

JavaScript로 이벤트 핸들러를 작성하는 예를 들어 봅니다.

1. fxml_example.fxml 파일에서 XML doctype 정의 뒤에 다음 구문을 추가합니다.

    <?language javascript?>

2. Button 마크업에서 이벤트 호출 구문을 다음과 같이 변경합니다.

    onAction="handleSubmitButtonAction(event);"

3. GridPane에서 fx:controller 속성을 제거하고 그 안에 <script> 태그를 넣고 JavaScript 함수를 직접 구현합니다.

    <GridPane xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
         <fx:script>
             function handleSubmitButtonAction() {
                 actiontarget.setText("Calling the JavaScript");
             }
         </fx:script>

혹은 JavaScript파일을 외부에 별도로 두고 (예를 들어 fxml_example.js) 다음과 같이 포함시킬 수도 있습니다.

   <fx:script source="fxml_example.js"/>

이제 어플리케이션을 실행하면 다음과 같이 버튼에 동작함을 볼 수 있습니다.


FXML안에 스크립트를 포함할 경우 IDE에서 디버깅을 지원하지 않을 수도 있으니 유의하기 바랍니다. 되도록이면 Java코드로 분리하는 것이 여러모로 좋습니다.

CSS로 어플리케이션에 스타일 입히기

마지막으로 CSS를 입혀서 어플리케이션을 근사하게 만들어 봅시다.

1. 스타일 시트를 만듭니다.
  a. Project 윈도우에서 login폴더에 오른쪽 버튼을 클릭하고 "New"를 선택한 다음 "Other"를 선택합니다.
  b. 대화상자에서 "Cascading Style Sheet"를 선택하고 Next를 클릭합니다.
  c. 이름으로 "Login"을 입력하고 Finish를 클릭합니다.
  d. Login.css 파일의 내용은 이전 글에서 작성한 적이 있습니다. 그 내용대로 입력하세요.

2. 회색톤 린넨 질감의 배경 이미지를 다운받습니다. 그리고 이것을 fxmlexample 폴더에 추가합니다.

3. fxml_example.fxml 파일을 열어서 stylesheet 태그를 아래와 같이 추가합니다. Login.css 파일명 앞에 @를 붙이는 것은 FXML파일과 같은 디렉토리에 해당 파일이 있음을 의미합니다.

 <stylesheets>
    <URL value="@Login.css" />
  </stylesheets>

</GridPane>

4. 아래와 같이 GridPane의 스타일 클래스를 "root"로 변경합니다.

<GridPane fx:controller="fxmlexample.FXMLExampleController" 
    xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10" 
    styleClass="root">

5. CSS에 지정된 #welcome-text ID에 부합하도록 Text 객체의 ID를 아래와 같이 변경합니다.

<Text id="welcome-text" text="Welcome" 
        GridPane.columnIndex="0" GridPane.rowIndex="0" 
        GridPane.columnSpan="2"/>

6. 이제 어플리케이션을 실행하면 다음과 같은 멋진 대화상자를 볼 수 있습니다.


더 많은 내용을 보려면

FXML의 전반적이고 자세한 소개를 보려면 Introduction to FXML 글을 참조하는 것이 좋습니다.

또한 Oracle에서 무료로 제공하는 JavaFX Scene Builder를 이용하면 텍스트 코딩 없이 UI 조작으로 FXML을 만들 수 있습니다. 시각적으로 레이아웃을 배치하고 드랙&드롭으로 컨트롤들을 배치할 수 있어서 매우 편리합니다.


JavaFX Scene Builder에 대한 자세한 내용은 Getting Started with JavaFX Scene Builder 글을 참조하세요. 혹은 JavaFX Scene Builder User Guide를 참조하세요.

특히 JavaFX Scene Builder로 CSS를 적용하는 법은 Skinning with CSS and the CSS Analyzer 글을 참조하세요.

>>> background.jpg 다운 받기
>>> Login.css 다운 받기

댓글 없음:

댓글 쓰기