01. JavaFX 개요

02. JavaFX 프로젝트 생성 및 실행

03. JavaFX 레이아웃

04. JavaFX 컨테이너

05. JavaFX 이벤트 처리

06. JavaFX 속성 감시와 바인딩

07. JavaFx 컨트롤

08. JavaFX 메뉴바와 툴바

09. JavaFX 다이얼로그

10. JavaFX CSS 스타일

11. JavaFX 스레드 UI 변경

12. 장면 이동과 애니메이션

13. JavaFX 과제

01. JavaFX 개요

JavaFX는 자바 기반 UI 애플리케이션Rich Client Application을 개발할 때 사용할 수 있는 그래픽과 미디어

모듈을 말한다. 자바의 UI 라이브러리의 변천 과정은 다음과 같다.

AWT

자바 언어가 탄생한 1995년에는 인터넷이 활성화되지 않았기 때문에 대부분의 클라이언트 애플리

케이션은 운영체제가 제공하는 네이티브native UI 컴포넌트를 이용해서 개발되었다. 그래서 Java 1.0

에 포함된 AWTAbstract Window Toolkit는 운영 체제가 제공하는 네이티브 UI 컴포넌트를 이용하는 자바

라이브러리였다. 그렇다 보니 자바 애플리케이션이 실행되는 운영체제에 따라 UI의 모양이 서로 달

랐고, 종류도 제한적이었다.

Swing

AWT의 다음 주자로 Swing이 등장했다. Swing의 중심 아이디어는 운영체제가 제공하는 네이티

브 UI 컴포넌트를 사용하지 말자는 것이었다. 즉 모든 운영체제상에서 동일한 UI를 갖도록, Swing

자신만의 UI (look and feel )를 갖도록 했다. 그렇다 보니 실행 성능이 느려지고, 메모리를 더 많

이 사용하게 되었다. 시간이 흘러 네이티브 UI가 애니메이션과 다양한 시각적인 효과를 내면서 사

용자는 Swing의 UI보다는 운영체제의 네이티브 UI를 더 선호하게 되었다. 그래서 Swing은 점점

네이티브 UI에 밀려나기 시작했다.

JavaFX

JavaFX는 Java 기반의 데스크톱, 모바일 및 임베디드 시스템을 위한 오픈 소스 차세대 UI 클라이

언트 애플리케이션 플랫폼이다. JavaFX는 자바 코드와 분리해서 스타일 시트(CSS )를 작성할 수

있기 때문에 테마를 쉽게 변경할 수 있고, 안드로이드처럼 화면 레이아웃과 비즈니스 로직을 분리할

수 있기 때문에 Swing보다는 편리하게 디자인을 할 수 있다는 장점이 있다. 다음은 JavaFX 애플리

케이션을 구성하는 파일 단위 요소를 보여준다.

JavaFX 애플리케이션 구성 요소

[레이아웃] 자바 코드 파일 또는 FXML 파일

[외관 및 스타일] CSS 파일

[비즈니스 로직] 자바 코드 파일

[리소스] 그림 파일 동영상 파일 …

JavaFX 설치

JavaFX는 Java 7과 Java 8에서는 JDK에 포함되어 있지만, Java 11부터 JDK에 포함되지 않고

오픈 소스 모듈로 진화하였다. 따라서 JavaFX를 사용하려면 별도의 설치 작업이 필요하다. JavaFX

모듈 설치 파일은 다음 URL에서 다운로드할 수 있다.

https ://gluonhq.com/products/javafx

장기 지원Long Term Support, LTS 버전을 받는 것이 좋기 때문에 21.0.1 [LTS] 버전을 선택한다. 운영체제

와 CPU 아키텍처는 사용하는 PC에 따라 선택하고, 타입은 SDK를 선택한다.

JavaFX 버전은 다운로드하는 시점에 따라 달라질 수 있다.

윈도우 운영체제용 설치 파일은 압축 파일(zip ) 형식으로 되어 있다. 압축을 해제하면 다음 폴더가

나온다.

javafx -sdk -21.0.1

이 폴더를 다음 경로로 이동시킨다.

C :\ThisisJava\javafx -sdk -21.0.1

javafx-sdk-21.0.1 폴더 내부를 보면 lib 폴더가 있다. 이 폴더에는 JavaFX에서 제공하는 모듈

파일(JAR )이 있다. 이들 모듈에 포함되어 있는 패키지와 패키지 내부의 클래스 및 인터페이스를 보

려면 API 도큐먼트가 필요하다. API 도큐먼트는 다음 URL에서 볼 수 있다. 학습할 때 언제든지 참

고할 수 있도록 웹 브라우저 북마크(즐겨찾기)에 추가해 두자.

https ://openjfx.io/javadoc/21

Scene Builder 설치

씬 빌더Scene Builder는 드래그 앤 드롭 사용자 인터페이스 디자인 방식으로 JavaFX의 화면 레이아웃

FXML 파일을 자동 생성해주는 오픈 소스 도구이다. Scene Builder 설치 파일은 다음 URL에서

다운로드할 수 있다.

https ://gluonhq.com/products/scene -builder

사용하는 PC의 운영체제에 따라 설치 파일을 다운로드한다.

설치 과정에서 윈도우 운영체제에서의 설치 경로를 다음과 같이 변경해 준다.

C :\ThisisJava\javafx -sdk -21.0.1\SceneBuilder

설치 완료 후 다음 파일을 더블 클릭하면 Scene Builder가 실행된다.

C :\ThisisJava\javafx -sdk -21.0.1\SceneBuilder\SceneBuilder.exe

Empty 템플릿을 선택하면 다음과 같은 FXML 편집기가 실행된다. 편집기 실행을 확인하고 우측

상단에 있는 종료 버튼(

)을 클릭해서 편집기를 닫는다.

e(fx)clipse 설치

e (fx )clipse는 JavaFX 개발도구를 제공해 주는 이클립스 플러그인이다. 프로젝트 생성 및 자동 완

성, FXML 파일을 Scene Builder로 열기 등의 기능을 제공하기 때문에 매우 편리하다.

이클립스 메뉴에서 [Help] - [Install New Software…]를 선택하고, [work with:]에 ‘https://

download.eclipse.org/efxclipse/updates-nightly/site/’를 입력하고 [Add…] 버튼을 클

릭하면 다음과 같은 [Add Repository] 대화상자를 확인할 수 있다. 대화상자의 [Name:]에

‘efxclipse’라고 입력하고 [Add] 버튼을 클릭해 설치한다.

e (fx )clipse는 3.10 버전 이상을 설치해야 하지만, 현재는 아직 nightly 버전에 해당하므로 정식 버전이 출시

되면 [Eclipse Marketplace…]에서 설치할 수 있을 것이다.

이클립스 메뉴에서 [Window] - [Preferences]를 선택한다. 왼쪽 항목에서 JavaFX를 선택하면

다음과 같이 SceneBuilder 실행 파일 경로와 JavaFX SDK 경로를 입력하는 내용이 나온다.

입력란에서 [Browse] 버튼을 클릭해 다음 경로의 파일을 선택해준다.

SceneBuilder executable

C :\ThisisJava\javafx -sdk -21.0.1\SceneBuilder\SceneBuilder.exe

JavaFX 11 + SDK

C :\ThisisJava\javafx -sdk -12.0.1\lib

[Apply and Close] 버튼을 클릭해 적용한다.

02. JavaFX 프로젝트 생성 및 실행

JavaFX 애플리케이션을 위한 프로젝트는 Java Project 또는 e (fx )clipse가 제공해주는 JavaFX

Project 위자드를 이용해서 생성할 수 있다. 처음 학습할 때에는 Java Project로 생성하는 것이 좋

고, JavaFX가 어느 정도 익숙해지면 JavaFX Project 위자드를 이용하는 것이 좋다.

프로젝트 생성 및 설정

이클립스 메뉴에서 [File] - [New] - [Java

Project]를 선택한다. [New Java Project]

대화상자의 [Project name]에 ‘thisisjava_

appendix_javafx’를 입력한다. JavaFX 프로

젝트는 JavaFX SDK가 제공하는 모듈을 참조

해야 하고, 이들 모듈에 대한 의존 설정을 해야

하므로 모듈 기술자가 포함된 프로젝트로 생

성해야 한다. 따라서 [Create module-info.

java file] 체크박스에 체크한다. [Module

name ]은 [Project name ]과 동일하게

‘thisisjava_appendix_javafx’라고 입력하고

[Finish] 버튼을 클릭한다.

JavaFX 모듈 참조 및 의존성 설정

JavaFX 프로젝트는 JavaFX SDK가 제공하는 모듈을 참조해야 한다. thisisjava_appendix_

javafx 프로젝트를 선택하고 마우스 오른쪽 버튼을 클릭하여 [Build Path] - [Configure Build

Path] 메뉴를 선택한다. [Libraries] 탭에서 Modulepath를 선택하고, [Add External JARs] 버

튼을 클릭한다.

다음 폴더에 있는 모든 모듈 파일(JAR )을 선택하고, 화살표를 눌러 확장한다.

C :\ThisisJava\javafx -sdk -21.0.1\lib

우리 책에서는 javafx.control, javafx.fxml, javafx.media 모듈을 사용하므로 모듈 기술자에서

이들 모듈에 대한 의존 설정을 해야 한다.

module-info.java

open module thisisjava_appendix_javafx { requires java.se; requires javafx.controls; requires javafx.fxml; requires javafx.media;

}

open 키워드가 추가된 이유는 JavaFX 애플리케이션이 실행될 때 클래스 리플렉션을 수행하기 때

문에 프로젝트 내부의 모든 클래스에 대해 리플렉션을 허용하기 위해서이다. java.se 집합 모듈은

JDK가 제공하는 모든 모듈을 사용하기 위해 추가하였다.

메인 클래스 작성

JavaFX 애플리케이션을 개발하려면 제일 먼저 메인 클래스를 작성해야 한다. 메인 클래스는 JavaFX

애플리케이션을 시작하는 관문이다. 메인 클래스는 추상 클래스인 Application을 상속받고, start ( )

메소드를 재정의해야 한다.

그리고 main ( ) 메소드에서는 Application의 launch ( ) 메소드를 호출해야 한다. launch ( ) 메

소드는 메인 클래스의 객체를 생성하고, 메인 윈도우를 생성한 다음 start ( ) 메소드를 호출하는 역할

을 한다.

AppMain.java

package sec02.exam01_application_start;

import javafx.application.Application;
import javafx.stage.Stage;

public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.show();   //윈도우 보여주기

}

public static void main(String[] args) {
launch(args);   //AppMain 객체 생성 및 메인 윈도우 생성

}

}

실행 결과

JavaFX는 윈도우를 무대(Stage )로 표현한다. launch ( )는 start ( ) 메소드를 호출할 때 메인 윈도

우를 primaryStage로 제공하는데, start ( )에서 primaryStage.show ( ) 메소드를 호출함으로써

메인 윈도우가 보여진다.

JavaFX 라이프사이클

JavaFx 애플리케이션은 Application.launch ( ) 메소드부터 시작해서 다음과 같은 순서로 진행된다.

Application.launch()

기본 생성자

init()

start()

사용

Platform.exit() 호출 또는 마지막 Stage 닫힘

stop()

종료

launch ( ) 메소드가 호출되면 메인 클래스의 기본 생성자를 호출해서 객체를 생성하고, 이어서 init ( )

메소드를 호출한다. init ( ) 메소드는 메인 클래스의 실행 매개값을 얻어 애플리케이션이 이용할 수

있도록 해준다. init ( )이 끝나고 나면, start ( ) 메소드를 호출해서 메인 윈도우를 실행시킨다.

JavaFX 애플리케이션이 종료되는 경우는 다음과 같이 세 가지가 있다.

① 마우스로 마지막 윈도우(Stage )의 우측 상단 닫기 버튼을 클릭했을 때

② 자바 코드로 마지막 윈도우(Stage )의 close ( ) 메소드를 호출했을 때

③ 자바 코드로 Platform.exit ( ) 또는 System.exit (0 )을 호출했을 때

JavaFX 애플리케이션은 종료되기 직전에 stop ( ) 메소드를 호출하는데, stop ( ) 메소드는 사용한

자원을 정리(파일 닫기, 네트워크 끊기)할 기회를 준다. init ( )과 stop ( ) 메소드는 옵션으로 필요

한 경우에 재정의해서 사용하면 된다.

주목할 점은 라이프사이클의 각 단계에서 호출되는 메소드는 서로 다른 스레드상에서 실행된다는

것이다. JVM이 생성한 main 스레드가 launch ( )를 실행하면 다음과 같은 이름을 가진 2개의 스레

드를 생성하고 시작시킨다.

① JavaFX -Launcher : init ( ) 실행

② JavaFX Application Thread : 메인 클래스 기본 생성자, start ( ) 및 stop ( ) 실행

JavaFX 애플리케이션에 윈도우(Stage )를 비롯한 UI 생성 및 수정 작업 그리고 이벤트 처리 등은

모두 JavaFX Application Thread가 관장한다. 그 이유는 JavaFX API는 스레드에 안전하지 않

아서 멀티 스레드가 동시에 UI를 생성하거나 수정하게 되면 문제가 발생하기 때문이다.

그래서 JavaFX Application Thread만 UI를 생성하거나 수정할 수 있도록 되어 있고, 다른 스레

드가 UI에 접근하게 되면 예외가 발생한다. init ( ) 메소드에서 UI를 생성하는 코드를 작성하면 안

되는데, 그 이유는 init ( ) 메소드가 JavaFX-Launcher 스레드에서 실행되기 때문이다.

다음 예제는 기본 생성자, init ( ), start ( ), stop ( ) 메소드가 어떤 스레드상에서 실행되는지 보여

준다.

AppMain.java

package sec02.exam02_application_lifecycle;

import javafx.application.Application;
import javafx.stage.Stage;

public class AppMain extends Application {
public AppMain() {
System.out.println(Thread.currentThread().getName()+": AppMain() 호출");

}

@Override
public void init() throws Exception {
System.out.println(Thread.currentThread().getName()+": init() 호출");

}

@Override
public void start(Stage primaryStage) throws Exception {
System.out.println(Thread.currentThread().getName()+": start() 호출");
primaryStage.show();

}

@Override
public void stop() throws Exception {
System.out.println(Thread.currentThread().getName()+": stop() 호출");

}








public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+": main() 호출");
launch(args);

}

}

실행 결과

main: main() 호출 JavaFX Application Thread: AppMain() 호출 JavaFX-Launcher: init() 호출 JavaFX Application Thread: start() 호출 JavaFX Application Thread: stop() 호출

메인 클래스 실행 매개값 얻기

init ( ) 메소드의 역할은 메인 클래스의 실행 매개값을 얻어 애플리케이션의 초기값으로 이용할 수

있도록 하는 것이다. 메인 클래스를 실행할 때 실행 매개값을 다음과 같이 주었다고 가정해 보자.

java AppMain –ip = 192.168.0.5 –port = 50001

Application.launch ( )는 main ( ) 메소드의 매개값을 그대로 넘겨 받는데, 이 매개값은 init ( ) 메

소드에서 다음과 같이 얻을 수 있다.

Parameters params = getParameters(); Map<String, String> map = params.getNamed(); String ip = map.get(“ip”); String port = map.get(“port”);

Parameters의 getNamed ( ) 메소드는 “ip”를 키로 해서 “192.168.0.5”를 저장하고, “port”를 키

로 해서 “50001”을 저장하는 Map 컬렉션을 리턴한다.

무대와 장면

JavaFX는 윈도우를 무대Stage로 표현한다. 무대는 한 번에 하나의 장면을 가질 수 있는데, JavaFX는

장면을 Scene으로 표현한다. 메인 윈도우는 start ( ) 메소드의 primaryStage 매개값으로 전달되

지만, 장면Scene은 직접 생성해야 한다.

Scene을 생성하려면 UI의 루트 컨테이너인 javafx.scene.Parent가 필요하다.

Scene scene = new Scene(Parent root);

Parent는 추상 클래스이기 때문에 하위 컨테이너 클래스

로 객체를 생성해서 제공해야 한다. 주로 javafx.scene.

layout 패키지의 컨테이너들이 사용된다. 실제로 UI 컨

트롤들이 추가되는 곳은 컨테이너이며, 컨테이너의 폭과

높이가 장면의 폭과 높이가 된다.

Stage(윈도우)

Scene(장면)

Parent(루트 컨테이너)

Label(UI 컨트롤)

Button(UI 컨트롤)

장면Scene을 생성한 후에는 윈도우Stage에 올려야 하는데,

Stage의 setScene ( ) 메소드를 사용한다. setScene ( )

메소드는 매개값으로 받은 Scene을 윈도우 내용으로 설정한다.

primaryStage.setScene(scene);

다음 예제는 Parent의 하위 클래스인 VBox 컨테이너를 이용해서 Scene을 생성하고 메인 윈도우

(primaryStage )의 장면으로 설정했다. VBox에는 Label과 Button 컨트롤을 배치했다.

AppMain.java

package sec02.exam03_stage_scene;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Scene;




import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;

public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
VBox root  =  new VBox();
root.setPrefWidth(350);
root.setPrefHeight(150);
root.setAlignment(Pos.CENTER);
root.setSpacing(20);

//Parent 하위 객체인 VBox 생성 //VBox의 폭 설정 //VBox의 높이 설정 //컨트롤을 중앙으로 정렬 //컨트롤의 수직 간격

Label label = new Label(); label.setText(“Hello, JavaFX”); label.setFont(new Font(50));

//Label 컨트롤 생성 //text 속성 설정 //font 속성 설정

Button button = new Button(); button.setText(“확인”); button.setOnAction(event -> Platform.exit()); //클릭 이벤트 처리

//Button 컨트롤 생성 //text 속성 설정

root.getChildren().add(label); root.getChildren().add(button);

//VBox에 Label 컨트롤 추가 //VBox에 Button 컨트롤 추가

Scene scene = new Scene(root);

//VBox를 (cid:1814)트 컨테이너로 해서 Scene 생성

primaryStage.setTitle(“AppMain”); //윈도우의 제목 설정 //윈도우에 장면 설정 primaryStage.setScene(scene); //윈도우 보여주기 primaryStage.show();

}

public static void main(String[] args) { launch(args);

}

}

실행 결과

VBox는 수직 방향으로 컨트롤을 배치하는 컨테이너로 먼저 Label을 배치하고 그 아래에 Button

을 배치했다. 28라인은 Button을 클릭했을 때 발생하는 ActionEvent를 처리한 것이다. Button

을 클릭하면 Platform.exit ( )를 호출해서 애플리케이션을 종료하도록 했다.

03. JavaFX 레이아웃

장면Scene에는 다양한 컨트롤이 포함되는데, 이들을 배치하는 것이 레이아웃Layout이다. 레이아웃을

작성하는 방법은 두 가지가 있다. 하나는 자바 코드로 작성하는 프로그램적 레이아웃이고, 다른 하

나는 FXML로 작성하는 선언적 레이아웃이다.

프로그램적 레이아웃

프로그램적 레이아웃이란 자바 코드로 UI 컨트롤을 배치하는 것을 말한다. 자바 코드에 익숙한 개발

자들이 선호하는 방식으로 컨트롤 배치, 스타일 지정, 이벤트 처리 등을 모두 자바 코드로 작성한다.

이 방법은 레이아웃이 복잡해지면 코드가 복잡해지고, 모든 것을 개발자가 직접 작성해야 하므로 디

자이너와 협력해서 개발하는 것도 어렵다. 개발을 완료한 후 간단한 레이아웃 변경을 하더라도 자바

소스를 수정하고 재컴파일해야 한다는 단점도 있다.

다음 예제는 옆의 화면과 같은 레이아웃을 프로그램적 방법으로 작성한 것이다. 텍스트를 입력할 수

있는 TextField 컨트롤과 클릭할 수 있는 Button 컨트롤

이 수평으로 나란히 배치되어 있기 때문에 루트 컨테이너로

HBox를 사용하였다.

AppMain.java

package sec03.exam01_programmatical_layout;

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.layout.HBox;
import javafx.geometry.Insets;
import javafx.scene.control.TextField;
import javafx.scene.control.Button;
import javafx.scene.Scene;
import javafx.collections.ObservableList;

public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
HBox hbox  =  new HBox();
//HBox 컨테이너 생성
hbox.setPadding(new Insets(10, 10, 10, 10));  //안쪽 여백 설정
hbox.setSpacing(10);

//컨트롤간의 수(cid:3235) 간격 설정

TextField textField = new TextField(); //TextField 컨트롤 생성 textField.setPrefWidth(200);

//TextField의 폭 설정

Button button = new Button(); button.setText(“확인”);

//Button 컨트롤 생성 //Button 글자 설정

ObservableList list = hbox.getChildren(); //HBox의 ObservableList 얻기 list.add(textField); list.add(button);

//TextField 컨트롤 배치 //Button의 컨트롤 배치

Scene scene = new Scene(hbox);

//화면의 (cid:1814)트 컨테이너로 HBox 지정

primaryStage.setTitle(“AppMain”); primaryStage.setScene(scene); primaryStage.show();

//윈도우 창 제목 설정 //윈도우 창에 화면 설정 //윈도우 창 보여주기

}

public static void main(String[] args) { launch(args);

}

}

FXML 레이아웃

FXML은 XML 기반의 마크업 언어로 JavaFX 애플리케이션의 UI 레이아웃을 자바 코드에서 분리

해서 태그로 선언하는 방법을 제공한다. 이 방법은 안드로이드Android 앱을 개발하는 방법과 유사한

데, XML로 레이아웃을 작성하고, 이벤트 처리 및 애플리케이션 로직은 자바로 작성한다.

태그로 레이아웃을 정의하기 때문에 태그에 익숙한 디자이너와 협업이 쉽고, 개발 완료 후 레이아웃

변경 시 자바 소스를 수정할 필요 없이 FXML 태그만 수정하면 되므로 편리하다. 그리고 레이아웃

이 비슷한 장면Scene들간에 재사용이 가능하기 때문에 개발 기간을 단축시킬 수도 있다.

다음 예제는 프로그램적 레이아웃을 사용하는 이전 예제를 FXML 레이아웃으로 변경한 것이다.

FXML 레이아웃은 root.fxml 파일로 다음과 같이 작성할 수 있다.

root.fxml

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

<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>

<HBox xmlns:fx ="http://javafx.com/fxml">
<padding>
<Insets top ="10" right ="10" bottom ="10" left ="10" />
</padding>
<spacing>10</spacing>
200 ```java

</HBox>


<!-- 자(cid:2343) 컨트롤 추가 -->
<!-- TextField 선언 -->
<!-- TextField의 폭 설정 -->

<!-- Button 컨트롤 선언 -->
<!-- Button 글자 설정 -->



루트 컨테이너인 HBox는 <HBox> 태그로 작성되고, fx 접두사에 대한 네임스페이스 선언

(xmlns:fx="http://javafx.com/fxml")이 추가되어 있는 것을 볼 수 있다. 추후 이것은 FXML

파일에 <fx:XXX> 형태의 태그 및 fx:xxx="값" 형태의 속성을 작성할 수 있다는 뜻이다.

HBox 컨테이너에 들어갈 TextField와 Button은 <children> 태그의 내용으로 각각 <TextField>

와 <Button> 태그로 작성된 것을 볼 수 있다. <padding>은 여백을 말하는데, 곧 이어서 학습한다.

다음은 자바 로직 부분을 작성한 메인 클래스이다.

>>> AppMain.java


```java
package sec03.exam02_fxml_layout;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Parent root  =  FXMLLoader.load(getClass().getResource("root.fxml"));
Scene scene  =  new Scene(root);

primaryStage.setTitle("AppMain");
primaryStage.setScene(scene);
primaryStage.show();

}

public static void main(String[] args) {
launch(args);

}

}

메인 클래스는 start ( ) 메소드에서 FXML 레이아웃 파일을 읽고 선언된 내용을 객체화해야 한다.

이것을 FXML 로딩이라고 한다. 이때 FXMLLoader의 load ( ) 메소드가 사용된다. load ( ) 메소

드는 정적과 인스턴스 모두 있다.

다음은 정적 load ( ) 메소드로 FXML 레이아웃 파일을 로딩하는 코드이다.

Parent root = FXMLLoader.load(getClass().getResource(“root.fxml”));

load ( )의 매개값은 FXML 파일의 위치 정보가 있는 URL 객체이다. FXML 파일이 메인 클래

스와 동일한 패키지에 있을 경우, getClass ( )로 메인 클래스 타입을 얻고 상대 경로를 이용해서

getResource ( )로 FXML 파일을 찾아 URL 객체를 얻을 수 있다.

다음은 인스턴스 load ( ) 메소드로 FXML 레이아웃 파일을 로딩하는 코드이다.

FXMLLoader loader = new FXMLLoader(getClass().getResource(“root.fxml”)); Parent root = (Parent)loader.load();

정적 또는 인스턴스 load ( ) 메소드가 리턴하는 타입은 Parent 타입인데, 실제 객체는 FXML 파일

에서 루트 태그로 선언된 컨테이너이다. 위 예제에서 FXML 파일의 루트 태그는 이므로 다

음과 같이 타입 변환이 가능하다.

HBox root = (HBox) FXMLLoader.load(getClass().getResource(“root.fxml”));

FXML 파일을 로딩해서 Parent 객체를 얻었다면 이것을 가지고 다음과 같이 장면Scene 객체를 생성

하면 된다.

Scene scene = new Scene(root);

레이아웃 여백: 패딩과 마진

컨트롤을 보기 좋게 배치하기 위해서 여백이 거의 필수적으로 들어간다. 여백은 패딩padding과 마진

margin이 있는데, 패딩은 안쪽 여백을 말하고 마진은 바깥 여백을 말한다.

패딩은 컨테이너의 setPadding ( ) 메소드를 사용해서 설정하는 반면, 마진은 바깥 컨테이너의

setMargin ( ) 메소드를 사용해야 한다. 예를 들어 Button이 HBox에 포함되어 있을 때 HBox에

서 패딩을 50으로 주는 것과 Button에서 마진을 50으로 주는 것은 동일한 결과를 얻지만 코드는

다르다.

구분

HBox의 패딩

Button의 마진

HBox

HBox

개념

자바

코드

FXML 태그

Button

Button

HBox hbox = new HBox(); hbox.setPadding(new Insets(50));

Button button = new Button(); HBox.setMargin(button, new Insets(50));

<HBox>
<padding>
<Insets topRightBottomLeft = "50"/>
</padding>
</HBox>

<Button>
<HBox.margin>
<Insets topRightBottomLeft = "50"/>
</HBox.margin>
</Button>

마진과 패딩은 적용하는 위치에 따라 top, right, bottom, left로 구분된다.

컨테이너A

컨테이너B

컨테이너A의 top padding

내용

컨테이너A의 right margin

내용

컨테이너B의 right padding

마진과 패딩값은 Insets 객체로 제공해야 하는데, 다음과 같이 생성한다. 생성자 매개값의 순서는

top을 시작으로 시계방향으로 나열되어 있어 쉽게 기억할 수 있을 것이다.

//top, right, bottom, left를 모두 동일한 값으로 설정할 때 new Insets(double topRightBottomLeft);

//top, right, bottom, left를 다(cid:1842) 값으로 설정할 때 new Insets(double top, double right, double bottom, double left)

다음 예제를 보면 14~17라인은 HBox의 안

쪽 여백 패딩으로 top과 left는 50, right와

bottom은 10으로 설정했다. 그리고 20~23라인

은 Button의 바깥 여백 마진으로 top과 right

는 10, bottom과 left는 50으로 설정했다. 한

쪽을 주석으로 처리해놓고 각각 실행해 보자.

AppMain.java

HBox Padding

Button Margin

package sec03.exam03_margin_padding;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
//패딩 설정-----------------------
/*HBox hbox  =  new HBox();
hbox.setPadding(new Insets(50, 10, 10, 50));
Button button  =  new Button();
button.setPrefSize(100, 100);*/

//마(cid:2771) 설정———————— HBox hbox = new HBox(); Button button = new Button(); button.setPrefSize(100, 100); HBox.setMargin(button, new Insets(10, 10, 50, 50));

hbox.getChildren().add(button);

Scene scene = new Scene(hbox);

primaryStage.setTitle(“AppMain”);

primaryStage.setScene(scene); primaryStage.show();

}

public static void main(String[] args) { launch(args);

}

}

FXML 태그와 자바 코드 매핑

FXML로 선언된 태그는 자바 코드로 변환되어 실행되기 때문에 자바 코드와 매핑 관계가 존재한다.

이 매핑 관계만 잘 이해하면 JavaFX API 도큐먼트를 참조해서 FXML 태그를 쉽게 작성할 수 있다.

다음은 프로그램적 레이아웃 자바 코드와 FXML 레이아웃 태그를 매핑시킨 표이다.

프로그램적 레이아웃 자바 코드

FXML 레이아웃 태그

HBox hbox = new HBox(); hbox.setPadding(new Insets(10,10,10,10)); hbox.setSpacing(10);

<HBox xmlns:fx = "http://javafx.com/fxml">
<padding>
<Insets top = "10" right = "10"
bottom = "10" left = "10"/>
</padding>
<spacing>10</spacing>
</HBox>

TextField textField  =  new TextField();
textField.setPrefWidth(200);

Button button  =  new Button();
button.setText("확인");

ObservableList list  =  hbox.getChildren();
list.add(textField);
list.add(button);

<TextField>
<prefWidth>200</prefWidth>
</TextField>

<Button >
<text(cid:31)확인(cid:29)/text>
</Button>

<children>
<TextField>...</TextField>
<Button >...</Button>
</children>

FXML은 XML 기반의 마크업 언어이기 때문에 XML 작성 규칙을 잘 지켜서 작성해야 한다. FXML

태그를 무조건 외우기보다는 자바 코드와 매핑되는 FXML 작성 규칙을 이해하면 FXML을 빨리 익

힐 수 있다. 그럼 FXML 작성 규칙을 자세히 살펴보기로 하자.

패키지 선언

자바 코드의 패키지 선언과 매핑되는 FXML 태그는 <?import?>이다. 클래스 하나를 import하는

방법과 같은 패키지의 모든 클래스를 import하는 방법은 다음과 같다.

자바 코드

FXML 태그

import javafx.scene.layout.HBox;

<?import javafx.scene.layout.HBox?>

import javafx.scene.control.*;

<?import javafx.scene.control.*?>

<?import?> 태그를 작성하는 위치는 정해져 있다. XML 선언 태그인 <?xml version="1.0"

encoding=”UTF-8”?>과 루트 컨테이너 태그 사이이다.

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

<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.*?>

(cid:29)(cid:1814)트컨테이너 xmlns:fx = “http://javafx.com/fxml” > …

(cid:29)/(cid:1814)트컨테이너(cid:31)

FXML 태그의 이름은 하나의 JavaFX API 클래스 이름과 매핑되기 때문에 해당 클래스가 존재하

는 패키지를 반드시 <?import?> 태그로 선언해야 한다. 그렇지 않으면 FXML을 로딩할 때 not a

valid type이라는 메시지와 함께 javafx.fxml.LoadException이 발생한다.

태그 선언

FXML 태그는 < 와 > 사이에 태그 이름을 작성한 것인데, 반드시 시작 태그가 있으면 끝 태그도 있어

야 한다. 그렇지 않으면 javax.xml.stream.XMLStreamException 예외가 발생한다.

(cid:29)태그이름(cid:31) … (cid:29)/태그이름(cid:31)

시작 태그와 끝 태그 사이에는 태그 내용이 작성되는데, 태그 내용이 없을 경우에는 다음과 같이 시

작 태그 끝에 />를 붙이고 끝 태그를 생략할 수 있다.

(cid:29)태그이름/(cid:31)

태그 이름은 JavaFX의 클래스명이거나, Setter의 메소드명이 될 수 있다. 다음 표에서 Button 컨

트롤을 자바 코드로 작성한 것과 FXML 태그로 작성한 것을 비교해보면 쉽게 이해가 될 것이다.

자바 코드

Button button = new Button(); button.setText(“확인”);

FXML

<Button>
<text(cid:31)확인(cid:29)/text>
</Button>

속성 선언

FXML 태그는 다음과 같은 속성을 가질 수 있다. 속성값은 큰따옴표(“) 또는 작은따옴표(‘)로 반드

시 감싸야 한다. 그렇지 않으면 javax.xml.stream.XMLStreamException 예외가 발생한다.

(cid:29)태그이름 속성명= “값” 속성명= (cid:8612)값(cid:8613)(cid:31) … (cid:29)/태그이름(cid:31)

속성명은 Setter 메소드명이 오는데, 모든 Setter가 사용될 수 있는 것은 아니고 기본 타입(boolean,

byte, short, char, int, long, float, double )의 값을 세팅하거나, String (문자열)을 세팅하는

Setter만 올 수 있다. 예를 들어 Button의 글자를 설정할 때 setText ( ) 메소드를 사용하는데, 매개

값이 문자열이므로 다음과 같이 text 속성으로 작성할 수 있다.

자바 코드

FXML (Setter 태그)

FXML (Setter 속성)

Button button = new Button(); button.setText(“확인”);

<Button >
<text(cid:31)확인(cid:29)/text>
</Button>

<Button text = "확인"/(cid:31)



객체 선언

Setter 메소드가 기본 타입과 String 타입이 아닌 다른 타입의 객체를 매개값으로 갖는다면 속성으

로 작성할 수 없고, 태그로 작성해야 한다. 이때 매개값인 객체를 태그로 선언하는 방법을 알아보자.

객체 선언

1) <클래스 속성="값">

일반적으로 다음과 같이 클래스명으로 태그를 작성하면 new 연산자로 기본 생성자를 호출해서 객

체가 생성된다.

(cid:29)클래스(cid:31)

만약 생성자에 매개변수가 있고, 매개변수에 @NamedArg (javafx.beans.NamedArg ) 어노테

이션이 적용되어 있다면 속성명이나 자식 태그로 작성할 수 있다.

(cid:29)클래스 속성= “값”(cid:31)

(cid:29)클래스(cid:31)

(cid:29)매개변수(cid:31)값(cid:29)/매개변수(cid:31)

(cid:29)/클래스(cid:31)

예를 들어 HBox의 패딩을 설정할 때 setPadding (Insets value ) 메소드를 사용하는데, Insets는

기본 생성자가 없고 Insets (double topRightBottomLeft ) 또는 Insets (double top, double

right, double bottom, double left )만 있다. 이 경우 Insets 객체를 FXML로 선언하면 다음과

같다.

자바 코드

HBox hbox = new HBox(); hbox.setPadding(new Insets(10,10,10,10));

FXML

<HBox>
<padding>
<Insets top = "10" right = "10"
bottom = "10" left = "10"/>
</padding>
</HBox>


2) <클래스 fx:value="값">

클래스가 valueOf (String ) 메소드를 제공해서 객체를 생성하는 경우가 있다. 예를 들어 String,

Integer, Double, Boolean 클래스는 valueOf (String )을 호출해서 객체를 생성한다. 이 경우

다음과 같이 FXML 태그를 작성할 수 있다.

(cid:29)클래스 fx:value = “값” />

예를 들어 String, Integer, Double, Boolean 객체를 FXML로 선언하면 다음과 같다.

자바 코드

FXML

String.valueOf(“Hello, World”); Integer.valueOf(“1”); Double.valueOf(“1.0”); Boolean.valueOf(“false”);

3) <클래스 fx:constant="상수">

클래스에 정의된 상수값을 얻고 싶을 경우에는 다음과 같이 FXML 태그를 작성할 수 있다.

(cid:29)클래스 fx:constant = “상수” />

예를 들어 Double.MAX_VALUE 상수값을 Button 컨트롤의 maxWidth 속성값으로 설정할 경

우 다음과 같이 FXML로 선언할 수 있다.

자바 코드

FXML

Button button = new Button(); button.setMaxWidth( Double.MAX_VALUE );

<Button>
<maxWidth>
<Double fx:constant = "MAX_VALUE"/>
</maxHeight>
</Button>

4) <클래스 fx:factory="정적메소드">

어떤 클래스는 new 연산자로 객체를 생성할 수 없고, 정적 메소드로 객체를 얻어야 하는 경우도 있

다. 이 경우 다음과 같이 FXML 태그를 작성할 수 있다.

(cid:29)클래스 fx:factory = “정적메소드”(cid:31)

예를 들어 ComboBox의 setItems (ObservableList value ) 메소드는 ObservableList 인

터페이스의 구현 객체를 매개값으로 가지는데, ObservableList 구현 객체는 FXCollections의 정

적 메소드인 observableArrayList (E… items ) 메소드로 얻을 수 있다. 그래서 다음과 같이 FXML

을 작성해야 한다.

자바 코드

FXML

ComboBox combo = new ComboBox(); combo.setItems( FXCollections. observableArrayList( “공개”, “비공개” ) );

<String fx:value = "공개"/(cid:31) <String fx:value = "비공개"/(cid:31)

Scene Builder 사용

지금까지 FXML 태그를 작성하는 방법을 학습했지만, 레이아웃이 복잡해질수록 태그로 직접 작성

한다는 것은 힘든 작업이다. 01절에서 설치한 Scene Builder를 이용해서 드래그 앤 드롭 방식으로

화면을 디자인하면 자동으로 FXML 파일이 생성되므로 매우 편리하다.

실제로 JavaFX 애플리케이션 개발은 전체 레이아웃 작업은 Scene Builder로 빠르게 진행하고,

간단한 수정 작업은 FXML 태그로 직접 작성하는 것이 일반적이다. 다음은 Scene Builder에서

FXML 레이아웃을 작성하는 모습을 보여준다.

이클립스에서 JavaFX Scene Builder를 실행하려면 먼저 FXML 파일을 생성해야 한다. Package

Explorer 뷰에서 sec03.exam04_scene_builder 패키지를 생성하고, 마우스 오른쪽 버튼을 클

릭한 후 [New] - [Other] - [JavaFX]로 들어가 [New FXML Document]를 선택한다.

FXML File 대화상자에서 Name 입력란에 생성할 파일명을 입력하고 Root Element에 루트 컨테

이너가 될 클래스를 선택한 후 [Finish] 버튼을 클릭하면 FXML 파일이 생성된다.

다음은 Root Element를 HBox로 선택하고 생성한 FXML 파일 내용을 보여준다.

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

<?import javafx.scene.layout.HBox?>

<HBox xmlns:fx = "http://javafx.com/fxml">
<!-- TODO Add Nodes -->
</HBox>

xmlns :fx=”http ://javafx.com/fxml/1”로 되어있어도 상관없음, 1은 FXML 버전을 뜻함

생성된 FXML 파일을 Scene Builder로 편집하려면 이클립스 Package Expolorer 뷰에서 FXML

파일을 선택하고 마우스 오른쪽 버튼을 클릭한 후 [Open with SceneBuilder]를 선택하면 된다.

Scene Builder 화면의 왼쪽에 있는 [Containers]와 [Controls] 메뉴에서 원하는 항목을 드래그

해 중앙의 디자인 영역에 드롭시키면 배치가 된다. 배치된 컨테이너와 컨트롤의 속성은 화면 오른쪽

에 있는 [Properties]와 [Layout] 메뉴에서 설정할 수 있다.

레이아웃 디자인이 완성했다면 상단 메뉴에서 [File] - [Save]를 선택해서 FXML 파일로 저장한다.

04. JavaFX 컨테이너

레이아웃을 작성할 때 컨트롤들을 쉽게 배치할 수 있도록 도와주는 클래스가 컨테이너이다. javafx.

scene.layout 패키지에는 다양한 컨테이너 클래스들이 존재한다. 접미사가 Pane으로 끝나는 클

래스는 모두 컨테이너이고, 그 이외에 Hbox, Vbox가 있다.

설명

컨트롤을 좌표를 이용해서 배치하는 레이아웃

위, 아래, 오른쪽, 왼쪽, 중앙에 컨트롤을 배치하는 레이아웃

행으로 배치하되 공간이 부족하면 새로운 행에 배치하는 레이아웃

그리드로 배치하되 셀의 크기가 고정적이지 않은 레이아웃

컨트롤을 겹쳐 배치하는 레이아웃

그리드로 배치하되 고정된 셀의 크기를 갖는 레이아웃

수평으로 배치하는 레이아웃

수직으로 배치하는 레이아웃

컨테이너

AnchorPane

BorderPane

FlowPane

GridPane

StackPane

TilePane

HBox

VBox

AnchorPane 컨테이너

AnchorPane 컨테이너는 좌표를 이용하여 AnchorPane의 좌상단(0, 0 )을 기준으로 컨트롤을 배

치한다. 컨트롤 좌표는 좌상단(layoutX, layoutY ) 값을 말하는데, (0, 0 )으로부터 떨어진 거리를

의미한다.

(0, 0)

30. (layoutY)

(layoutX)

(50, 30)

AnchorPane

컨트롤

AnchorPane에서 사용할 수 있는 주요 설정은 다음과 같다.

태그 및 속성

prefWidth

prefHeight

layoutX

layoutY

설명 폭을 설정 높이를 설정 컨트롤의 X 좌표 컨트롤의 Y 좌표 컨트롤을 포함 적용 AnchorPane AnchorPane 컨트롤 컨트롤 AnchorPane AnchorPane 컨테이너는 JavaFX Scene Builder를 사용해서 디자인하는 것이 좋다. 눈으로 거리 를 확인해서 컨트롤을 드롭시킬 수 있기 때문이다. 프로그램적 레이아웃 방법은 직접 코드상에 좌표 값을 입력해야 하기 때문에 매우 불편하다. 다음 예제는 AnchorPane루트 컨테이너를 사용해서 로그인 레이아웃을 디자인한 것이다. >>> root.fxml ```java