JavaFX FXML GUI 디자인

JavaFX는 “FXML"라는 단순 언어를 사용하는 것으로, XML을 사용해 쉽게 GUI를 디자인 할 수 있다. 이 FXML의 기본적인 사용법에 대해 설명한다.

FXML이란?

JavaFX는 풍부한 GUI를 가진 응용 프로그램을 빠르게 개발할 수 있는 것을 중시하고 새롭게 만들어진 GUI 라이브러리이다. 하지만, 지금까지 본 바로는 “Swing과 별로 변함이 없다"라는 인상을 받았을 것이다. 액션 이벤트의 설정 등은 간단했지만 “이정도라면 일부러 Swing에서 마이그레이션 할 필요도 없다"고 느낀 사람도 많을 것이다.

사실 JavaFX는 Java 클래스를 만들어 Java 소스 코드에서 GUI를 만드는 방식은 그다지 많이 하지는 않다. 그 이상으로 중요시되고 있는 것이 “FXML"라는 것을 이용한 GUI 디자인이다.

FXML은 JavaFX에 포함되어 있는 XML 기반의 GUI 기술 언어이다. “언어"라고 하지만 XML 기반의 마크 업 언어이기 때문에, Java 소스 코드를 작성하는 것보다 기술은 매우 간단하다. 이용하는 컨테이너 및 컨트롤의 내용을 XML 기반으로 기술하는 것만으로 GUI를 디자인 할 수 있다. 이후에는 Java 측에 이것을 읽어 표시하는 짧은 코드를 작성하는 것만으로 본격적인 GUI 애플리케이션을 만들 수 있다.

JavaFX Scene Builder에 대해

FXML가 XML 기반으로 기술 할 수 있는 장점은 단순히 “쓰기 쉽다” 것만 있는 것이 아니다. XML 기반이기 때문에 분석이 쉽고, 각종 도구 등에 의해 디자인이 쉽다는 것도 있다. 실제로 Java의 개발 업체인 오라클에서는 “JavaFX Scene Builder"라는 FXML에 따르는 GUI 디자인 도구를 제공하고 있다.

이 도구는 마우스로 부품을 드래그하여 배치하는 것만으로 FXML 소스 코드를 작성할 수 있는 도구이다. FXML의 이용은 이러한 GUI 디자인 도구의 사용이 필수적이라고 할 수 있다. 우선 이러한 도구를 사용할 수 있도록 해두자.

JavaFX Scene Builder 다운로드 위치 :

http://www.oracle.com/technetwork/java/javase/downloads/javafxscenebuilder-info-2157684.html

FXML 기본 코드

그럼 FXML 파일이라고 하는 것은 어떤 형태로 되어있는 것일까? 그 기본 코드를 대충 이해하도록 하자.

FXML는 “.fxml” 확장자의 텍스트 파일로 작성된다. 이 소스 코드의 기본형을 정리하면 대충 다음과 같이 될 것이다.

FXML의 기본형

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

<?import XX ?>

<Pane클래스
   xmlns="..." 
   xmlns:fx="...">
   <!-- Pane에 통합 컨트롤 종류 -->
</Pane클래스>

<?xml ?> 후에, Java의 import 문에 해당하는 <?import ?> 태그를 제공한다. 이것으로 사용하는 클래스를 import 할 수 있다. <?import ?> 태그를 쓰지 않으면, 클래스는 모든 풀 패키지를 지정하고 쓰지 않으면 갈 수 없게 되므로 주의하자.

FXML의 루트 태그(최상위 태그)는 Pane 클래스의 태그에 의해 작성된다. 예를 들어 BorderPane라면, <BorderPane>라는 태그를 작성하고 그 안에 컨트롤 등의 태그를 작성한다.

기본적으로 FXML에는 Java 클래스 이름을 그대로 태그 이름으로 쓰면 대개 인식 할 수 있다. Label이라면 <Label> 형태이다.

루트 태그에는 xmlns(XML 네임 스페이스) 특성을 작성한다. 또한 FXML 자신의 네임 스페이스 특성으로 xmlns:fx도 제공한다. 이 두 속성에 의해 이 XML 코드가 FXML 코드로 인식되게 된다.

사실 단순히 Java 코드 내에서 FXML 파일을 로드하여 사용할 뿐이라면, 이러한 네임 스페이스 특성은 필요 없게 된다. 쓰지 않아도 제대로 Java 내에서 FXML를 로드하여 사용할 수 있다.

이것들은 GUI 작성 도구 등을 사용하는 것이 될 수 있으면 좋지만, 구지 없어도 상관은 없다. 아무튼 FXML 기본으로 써놓도록 습관화하도록 하자.

아래 예제는 FXML의 기본 코드이다.

<?import XX ?>
 
<Pane클래스
    xmlns="http://javafx.com/javafx/8"
    xmlns:fx="http://javafx.com/fxml/1">
   <!-- Pane에 통합 컨트롤 종류 -->
</Pane클래스>

FXML 사용

FXML 사용한 간단한 샘플을 작성해 보자. 아래 코드가 FXML 예제이다.

<?xml version="1.0" encoding="UTF-8"?>
 
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
 
<BorderPane
    xmlns="http://javafx.com/javafx/8"
    xmlns:fx="http://javafx.com/fxml/1">
    <top>
        <Label text="This is FXML!" />
    </top>
</BorderPane>

이것은 Label을 한개를 BorderPane에 추가한 단순한 것이다. 다음 내용을 설명은 아래와 같다.

<? import?> 태그

처음에 여러개의 <? import?> 문이 작성되어 있다. 사실 이것은 JavaFX Scene Builder에서 만들 때 자동으로 추가되는 것이다. Pane 및 컨트롤러 관계의 클래스를 대충 import 문장을 준비하고 있는 것이다.

<BorderPane> 태그

여기에서는 <BorderPane>라는 태그를 있고, 이 가운데에 <Label> 태그를 작성되어 있다. 이를 잘 보면 바로 작성 된 것이 아니라, <Top>라는 태그에 둘려 싸여 있는 것을 알 수 있다. 이것은 <BorderPane> 특유의 것으로, 결합되는 위치를 나타내는 태그에서 컨트롤의 태그를 작성하도록 되어있는 것이다. 준비되어 있는 것은 다음의 5가지가 있다.

<BorderPane>의 위치 태그

<Top>, <Bottom>, <Right>, <Left>, <Ceter>

<Label> 태그

<Label> 태그에서 text라는 속성에 표시할 텍스트를 지정한다. 컨트롤의 속성은 이 처럼 속성을 태그에 작성할 수 있다. text="OO"라는 것은 setText("OO")같은 기능을 하고 있다고 생각하면 이해하기 쉬울 것이다.

이와 같이 컨트롤으로 속성를 설정하는 setOO(값) 처리는 그대로 컨트롤 태그의 “OO=값"와 같은 형태로 고쳐 쓸 수 있는 것이 많다. 이것은 Label뿐만 아니라 컨트롤 전반이라고 할 수 있는 것이다.

FXML 로드

FXML은 당연하지만 작성만으로는 동작하지 않는다. Java 클래스 측에서 이를 로드하고 인스턴스화해서 윈도우에 포함되어 처음에 사용할 수 있게 된다. 이것은 일반적으로 Application 클래스의 start 메소드로 처리를 작성하는 작업을 한다. 그럼 FXML 이용에 흐름을 정리하자.

Pane 로드

FXML을 로드해서 Pane 인스턴스를 생성한다. 이것은 javafx.fxml 패키지에 있는 FXMLLoader라는 클래스를 이용한다. 이 중에 “load"클래스 메소드를 호출하는 것으로, FXML 파일을 로드하고 인스턴스를 얻을 수 있다.

FXMLLoader.load(URL)

인수에는 URL 인스턴스를 전달한다. FXML 파일이 Application 클래스와 동일한 위치에 저장되어 있는 경우, Class의 getResource메소드로 로드하는 것이 좋다. 즉, 아래와 같은 방식이다.

변수 = FXMLLoader.load(getClass().getResource("xxx.fxml"));

xxx.fxml에 로드할 FXML 파일 이름을 지정하면 된다. 주의 할 점은 취득되는 인스턴스이다. 이것은 Object 인스턴스에 캐스팅되어 있다. 그러므로 취득 후에 본래의 Pane 인스턴스로 변환하여 이용할 수 있다.

Scene에 추가

FXML에서 Pane 인스턴스를 취득했다면, 다음은 간단하다. Scene 인스턴스를 생성할 때 이 Pane을 인수 지정해서 Scene을 준비하고, Stage에 설정하여 사용하면 된다.

Scene 변수 = new Scene(Pane인스턴스, 폭, 높이);

Scene은 이런 식으로 인스턴스를 생성 할 수 있다. FXML를 이용해도 Pane 인스턴스만 얻을 수 있으면, 다음은 다르지 않다.

아래는 실제 사용 예제이다.

package com.devkuma.javafx;
     
import java.io.IOException;
 
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
 
public class App extends Application {
     
    public static void main(String[] args) {
        launch(args);
    }
 
    @Override
    public void start(Stage stage) {
            BorderPane root;
            try {
                root = (BorderPane)FXMLLoader.load(getClass().getResource("app.fxml"));
                Scene scene = new Scene(root,200,100);
                stage.setScene(scene);
                stage.show();
            } catch (IOException e) {
                e.printStackTrace();
            }
    }
}

여기에서는 com.devkuma.javafx.App 클래스와 같은 위치에 app.fxml라는 파일명으로 FXML 파일이 배치되어 있다라는 전제에서 코드를 작성하고 있다. 실제로 이전에 FXML 파일을 배치해서 이동해 보자. 그러면 제대로 창이 나타날 것이다!

스타일 시트를 이용

컨트롤의 섬세한 표시 설정은 FXML에 속성으로 작성해서 넣는 것보다 좀 더 알아보기 쉽게 하는 방식이 있다. 그것은 “스타일 시트"를 사용하는 것이다. JavaFX에는 컨트롤의 속성을 스타일 시트부터 읽어서 적용시키는 기능을 가지고 있다. 이를 이용하여, 컨트롤의 표시를 Java 코드로 작성하지 않고도 쉽게 구성 할 수 있다.

스타일 시트의 사용은 매우 간단하다. FXML의 Pane 태그 안에 다음과 같은 형태로 스타일 시트를 작성하면 된다.

<stylesheets>
    <URL value="@스타일 시트"/>
</stylesheets>

<stylesheets> 태그 중에 로드하는 스타일 시트의 URL을 제공한다. 이것은 필요한 만큼 쓸 수 있다. <URL> 태그에는 value로 로드하는 스타일 시트의 경로를 지정한다. 이것은 “@파일 이름"과 같이 작성함으로써 FXML 파일과 같은 위치에 있는 스타일 시트 파일을 지정할 수 있다.

아래 소스 코드는 이전에 FXML에 스타일 시트를 추가한 것이다.

<?xml version="1.0" encoding="UTF-8"?>
 
<?import java.net.URL ?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
 
<BorderPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
    <stylesheets>
        <URL value="@app.css" />
    </stylesheets>
    <top>
        <Label fx:id="label1" text="This is FXML!" />
    </top>
</BorderPane>

이것으로 FXML 파일과 같은 위치에 있는 “app.css"을 로드한다. 여기에서는 스타일 시트를 적용하기에 <Label> 태그에 ID를 지정하고 있다.

<Label fx:id="label1"... />

이와 같이 “fx:id"이라고 작성하는 것으로 그 컨트롤에 ID를 지정할 수 있다. 스타일 시트에는 이 ID를 사용하여 특정 컨트롤에 스타일을 적용 할 수 있다.

스타일 시트 작성

그럼 스타일 시트를 작성해 보자. 먼저 FXML 파일과 같은 위치에 “app.css"라는 파일명으로 생성하자.

그리고 파일 안에 FXML에 작성한 <Label fx:id="label1"/> 컨트롤의 스타일을 여기에 작성한다. 아래 코드가 간단한 예제이다.

@CHARSET "utf-8";
 
Label#label1 {
    -fx-font-family:Serif;
    -fx-font-size:24pt;
}

문자 코드의 지정

시작 부분에 @CHARSET "utf-8";이 작성되어 있다. 기본적으로 스타일 시트는 UTF-8로 인코딩한다. 이것은 필수는 아니지만, 작성하는 것이 기본이라고 생각하자.

컨트롤러의 스타일

여기에서는 label1이라는 ID의 Label에 대해 스타일이 작성되어 있다.

Label#label1 {...}

이와 같이 스타일의 지정은 “클래스 이름#ID 이름"와 같은 형식으로 스타일을 작성한다. HTML 스타일 이 “태그명#ID"이라고 작성하는 것과 같은 느낌이다.

물론 클래스 이름만 지정하면, 그 클래스의 컨트롤 전부 스타일을 적용 할 수 있다. 예를 들어, Label {...} 라고 하면, 모든 Label의 스타일을 모와서 설정할 수 있다.

폰트 속성에 대해

여기에서는 두 가지 스타일이 작성되어 있다. -fx-font-family-fx-font-size이다. 이것들은 폰트 패밀리 이름과 폰트 크기를 지정하는 것이다.

JavaFX의 스타일은 모두 이 처럼 -fx-OO으로 시작하는 이름으로 되어 있다. 대부분은 HTML로 사용되고 있는 스타일 이름 앞에 -fx-를 붙이는 것만으로 사용할 수 있지만, 반드시 다 그런건 아니다.

예를 들어, 배경 색상은 -fx-background-color 이지만, 텍스트의 색상은 -fx-text-fill으로 되어 있다. JavaFX 자체의 값도 있으므로 주의하도록 하자.




최종 수정 : 2017-09-19