Difference between revisions of "컴퓨터프로그래밍및실습 (2022년)/1128"

From DISLab
Jump to navigation Jump to search
(Replaced content with "== JavaFX 컨트롤 == == JavaFX 메뉴바(MenuBar)와 툴바(Toolbar) == == JavaFX 다이얼로그(Dialog) ==")
Tag: Replaced
Line 1: Line 1:
== JavaFX 개요 ==
# AWT
#* Native UI 컴포넌트 사용
#* 운영체제 마다 UI 모양이 다름
# Swing
#* 운영체제가 제공하는 native UI 사용 안 함
#* 운영체제가 새롭게 제공하는 UI 지원의 어려움
# JavaFX
#* Abode의 flash, Microsoft의 silverlight의 대항마
#* JDK 7부터 지원. JDK 8 권장.
#* JDK 11부터는 별도로 설치해야 함.
#* 화면 레이아웃과 스타일, 애플리케이션 로직 분리
#* Java 코드와 분리해서 스타일 시트(CSS)로 외관 작성 → 개발자와 디자이너의 동시 개발 가능
#* Java 코드에서도 레이아웃과 애플리케이션 로직을 분리하고 싶다면 레이아웃은 FXML로 작성, 로직은 Java로 작성
#* JavaFX 애플리케이션 구성 요소
{| class="wikitable" cellpadding="50"
| rowspan="2" align="center" | '''[레이아웃]'''<br/>자바 코드 파일</br>또는 FXML 파일
| align="center" | '''[외관 및 스타일]'''<br/>CSS 파일
| rowspan="2" align="center" | '''[리소스]'''<br/>그림 파일<br/>동영상 파일<br/>...
|-
| align="center" | '''[비즈니스 로직]'''<br/>자바 코드 파일
|}
== JavaFX 애플리케이션 개발 시작 ==
=== 메인 클래스 ===
<syntaxhighlight lang="java">
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);
    }
}
</syntaxhighlight>
=== JavaFX 라이프사이클(life cycle) ===
* 예제 코드
<syntaxhighlight lang="java">
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) throws Exception {
        System.out.println(Thread.currentThread().getName() + ": main() 호출");
        launch(args);       
    }
}
</syntaxhighlight>
* 실행 결과
<syntaxhighlight lang="text">
main: main() 호출
JavaFX Application Thread: AppMain() 호출
JavaFX-Launcher: init() 호출
JavaFX Application Thread: start() 호출
JavaFX Application Thread: stop() 호출 ← 프로그램을 끝내야 실행된다.
</syntaxhighlight>
=== 메일 클래스 실행 매개값 얻기 ===
<syntaxhighlight lang="text">
C:> java AppMain --ip=192.168.0.5 --port=50001
</syntaxhighlight>
* main()에서 launch(args)를 넘겨 받음
* init() 메소드에서 아래와 같이 실행할 수 있음
<syntaxhighlight lang="java">
Parameters params = getParameters();
List<String> list = params.getRaw();
Map<String, String> map = params.getNamed();
</syntaxhighlight>
=== 무대(Stage)와 장면(Scene) ===
* 윈도우 : Stage
* Stage에는 하나의 Scene을 가질 수 있음
* Scene은 직접 생성해야 함
[[file:CP2022_ch17.2_AppMain.png|thumb|AppMain 실행화면]]
<syntaxhighlight lang="java">
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);
        Label label = new Label();
        label.setText("Hello, JavaFX");
        label.setFont(new Font(50));
        Button button = new Button();
        button.setText("확인");
        button.setOnAction(event->Platform.exit());
        root.getChildren().add(label);
        root.getChildren().add(button);
        Scene scene = new Scene(root); // VBox를 루트 컨테이너(root container)로 해서 Scene 생성
        primaryStage.setTitle("AppMain입니다");
        primaryStage.setScene(scene); // 윈도우에 장면 설정
        primaryStage.show();       
    }
   
    public static void main(String[] args) {
        launch(args);
    }
}
</syntaxhighlight>
== JavaFX 레이아웃 ==
=== 프로그램적 레이아웃 ===
[[file:CP_2022_ch17.3.1.AppMain.png | thumb | 프로그램적 레이아웃]]
<syntaxhighlight lang="java">
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
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(10));
        hbox.setSpacing(10);
        TextField textField = new TextField();
        textField.setPrefWidth(200);
        Button button = new Button();
        button.setText("확인");
        ObservableList list = hbox.getChildren();
        list.add(textField);
        list.add(button);
        Scene scene = new Scene(hbox);
        primaryStage.setTitle("AppMain");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }
}
</syntaxhighlight>
=== FXML 레이아웃 ===
* FXML 파일 (root.fxml)
[[file:CP_2022_ch17.3.1.AppMain.png | thumb | FXML 레이아웃]]
<syntaxhighlight lang="text">
<?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>
    <children>
        <TextField>
            <prefWidth>200</prefWidth>
        </TextField>
        <Button>
            <text>확인</text>
        </Button>
    </children>
</HBox>
</syntaxhighlight>
* AppMain 클래스
<syntaxhighlight lang="java">
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")); // ← FXML 로드
        Scene scene = new Scene(root);
        primaryStage.setTitle("AppMain");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
   
    public static void main(String[] args) {
        launch(args);
    }
}
</syntaxhighlight>
=== 레이아웃(layout) 여백: 패딩(padding)과 마진(margin) ===
<table2 class=wikitable head=top sep=bar align=cll>
구분      | HBox의 패딩                                                        | Button의 마진
개념      | [[file:CP-17.3.3-padding.png]]                                    | [[file:CP-17.3.3-margin.png]]
자바 코드 | HBox hbox = new HBox();<br/>'''hbox.setPadding(new Insets(50))'''; | Button button = new Button();<br/>'''HBox.setMargin(button, new Insets(50));'''
FXML 태그 | <HBox><br/>{{sp2}}<padding><br/>{{sp2}}{{sp2}}<Insets topRightBottomLeft="50"/><br/>{{sp2}}</padding><br/></HBox> | <Button><br/>{{sp2}}<HBox.margin><br/>{{sp2}}{{sp2}}<Insets topRightBottomLeft="50"/><br/>{{sp2}}</Hbox.margin><br/></Button>
</table2>
* Margin, Padding 설정 방법
<syntaxhighlight lang="java">
// top, right, bottom, left를 모두 동일한 값으로 설정할 때
// Insets(double topRightBottomLeft)
new Insets(50);
// top, right, bottom, left를 다를 값으로 설정할 때
// Insets(double top, double right, double bottom, double left)
new Insets(10, 20, 30, 40);
</syntaxhighlight>
* 패딩과 마진 적용 예
[[file:CP-17.3.3-AppMain.png|thumb|패딩과 마진 적용 결과]]
<syntaxhighlight lang="java">
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);
       
        // 마진 설정
        // 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);
    }
}
</syntaxhighlight>
=== FXML 작성 규칙 ===
<table2 class=wikitable head=top sep=bar align=ll>
프로그램적 레이아웃 자바 코드 | FXML 레이아웃 태그
HBox hbox = new HBox();<br/>hbox.setPadding(new Inset(10, 10, 10, 10));<br/>hbox.setSpacing(10);<br/><br/>{{sp2}}    |    <HBox xmlns:fx="http://javafx.com/fxml"><br/>{{sp2}}<padding><br/>{{sp2}}{{sp2}}<Insets top="10" right="10" bottom="10" left="10"/><br/>{{sp2}}</padding><br/></HBox>
TextField textField = new TextField();<br/>textField.setPrefWidth(200);<br/>{{sp2}}    |    <TextField><br/>{{sp2}}<prefWidth>200</prefWidth><br/></TextField>
Button button = new Button();<br/>button.setText("확인");<br/>{{sp2}}  |  <Button><br/>{{sp2}}<text>확인</text><br/></Button>
ObsetvableList list = hbox.getChildren();<br/>list.add(textField);<br/>list.add(button);<br/><br/><br/><br/><br/>{{sp2}}    |    <children><br/>{{sp2}}<TextField><br/>{{sp2}}{{sp2}}<prefWidth>200</prefWidth><br/>{{sp2}}</TextField><br/>{{sp2}}<Button><br/>{{sp2}}{{sp2}}<text>확인</text><br/>{{sp2}}</Button><br/></children>
</table2>
==== 패키지(package) 선언 ====
<table2 class=wikitable head=top sep=bar align=ll>
자바 코드                          |  FXML 태그
import javafx.scene.layout.HBox;  |  <?import javafx.scene.layout.HBox?>
import javafx.scene.control.*;    |  <?import javafx.scene.control.*?>
</table2>
* <?import?>가 들어가는 위치는 <?xml ...>와 루트 컨테이너 태그 사이이다.
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.*?>
<루트 컨테이너 xmlns:fx="http://javafx.com/fxml">
...
</루트 컨테이너>
</syntaxhighlight>
* import를 제대로 하지 않으면 not a valid type 메시지와 함께 javafx.fxml.LoadException 발생
==== 태그(tag) 선언 ====
* 시작 태그와 끝 태그가 매칭되어야 한다.
<table2 class=wikitable head=top sep=bar align=ll>
자바 코드                                                              |  FXML
Button button = new Button();<br/>button.setText("확인");<br/>{{sp2}}  |  <Button><br/>{{sp2}}<text>확인</text><br/></Button>
</table2>
==== 속성(attribute) 선언 ====
* 속성은 "나 '로 감싸야 한다.
<syntaxhighlight lang="xml">
<태그이름 속성명="값" 속성명='값'> ... </태그이름>
</syntaxhighlight>
* 속성명은 Setter 메소드 명이 옴
* 모든 Setter가 사용될 수 있는 것은 아님. 기본 타입(boolean, byte, short, char, int, long, float, double)의 값을 세팅하거나, String을 세팅하는 Setter만 올 수 있음
* 예
<table2 class=wikitable head=top sep=bar align=lll>
자바 코드                                                              |  FXML (Setter 태그)                                    | FXML (Setter 속성)
Button button = new Button();<br/>button.setText("확인");<br/>{{sp2}}  |  <Button><br/>{{sp2}}<text>확인</text><br/></Button>  | <Button text="확인"/><br/><br/>{{sp2}}
</table2>
==== 객체 선언 ====
===== 클래스 속성 =====
* 생성자에 매개 변수가 있고, 매개 변수에 @NamedArg(javafx.beans.NamedArg) 어노테이션이 적용되어 있으면 속성명이나 자식 태그로 작성할 수 있음
<table2 class=wikitable sep=bar align=ll>
<클래스 매개변수="값"><br/><br/>{{sp2}}    | <클래스><br/>{{sp2}}<매개변수>값</매개변수><br/></클래스>
</table2>
* 예
** HBox를 패딩할 때 setPadding(Insets value) 메소드를 사용하는데
** Insets는 기본 생성자가 없고,
** Insets(double topRightBottomLeft) 또는 Insets(double top, double right, double bottom, double left)만 있음
** 이 경우 아래와 같이 선언 가능함
<table2 class=wikitable head=top sep=bar align=ll>
프로그램적 레이아웃 자바 코드 | FXML 레이아웃 태그
HBox hbox = new HBox();<br/>hbox.setPadding(new Inset(10, 10, 10, 10));<br/>hbox.setSpacing(10);<br/><br/>{{sp2}}    |    <HBox><br/>{{sp2}}<padding><br/>{{sp2}}{{sp2}}<Insets top="10" right="10" bottom="10" left="10"/><br/>{{sp2}}</padding><br/></HBox>
</table2>
===== 클래스 fx:value =====
* 클래스가 valueOf(String) 메소드를 제공하는 경우
<syntaxhighlight lang="xml">
<클래스 fx:value="값" />
</syntaxhighlight>
<table2 class=wikitable head=top sep=bar align=ll>
기본 코드  | FXML
String.valueOf("Hello, World!");<br/>Integer.valueOf("1");<br/>Double.valueOf("1.0");<br/>Boolean.valueOf("false");  |  <String fx:value="Hello, World!"/><br/><Integer fx:value="1"/><br/><Double fx:value="1.0"/><br/><Boolean fx:value="false"/>
</table2>
===== 클래스 fx:constant =====
*클래스에 정의된 상수값을 얻고 싶을 경우
<syntaxhighlight lang="xml">
<클래스 fx:constant="상수" />
</syntaxhighlight>
<table2 class=wikitable head=top sep=bar align=ll>
기본 코드  | FXML
Button button = new Button();<br/>button.setMaxWidth( Double.MAX_VALUE );<br/><br/><br/>{{sp2}}  | <Button><br/>{{sp2}}<maxWidth><br/>{{sp2}}{{sp2}}<Double fx:constant="MAX_VALUE"/><br/>{{sp2}}</maxWidth><br/><Button>
</table2>
===== 클래스 fx:factory =====
* 어떤 클래스는 new 연산자로 객체를 생성할 수 없고,
* 정적 메소드(이를 factory 메소드라 부른다)로 객체를 얻어야 하는 경우가 있음
<syntaxhighlight lang="xml">
<클래스 fx:factory="정적메소드" />
</syntaxhighlight>
* 예: <code>ObservableList</code>의 구현 객체는 <code>javafx.collections.FXCollections</code>의 정적 메소드인 <code>observableArrayList(E... items)</code> 메소드로 얻을 수 있다.
<table2 class=wikitable head=top sep=bar align=ll>
기본 코드  | FXML
ComboBox combo = new ComboBox();<br/>combo.setItems(FXCollections.observableArrayList("공개", "비공개"));<br/><br/><br/><br/><br/><br/>{{sp2}}  | <ComboBox><br/>{{sp2}}<Items><br/>{{sp2}}{{sp2}}<FXCollections fx:factory="observableArrayList"><br/>{{sp2}}{{sp2}}{{sp2}}<String fx:value="공개"/><br/>{{sp2}}{{sp2}}{{sp2}}<String fx:value="비공개"/><br/>{{sp2}}{{sp2}}</FXCollections><br/>{{sp2}}</items><br/></ComboBox>
</table2>
=== FXML 로딩과 Scene 생성 ===
* FXML 파일을 작성한 후 이를 이용하여 객체를 만들어야 한다. 이를 '''FXML loading'''이라고 한다.
* javafx.fxml.FXMLLoader를 이용
** 두 개의 load() 메소드 : 정적 메소드, 인스턴스 메소드
<syntaxhighlight lang="java">
Parent root = FXMLLoader.load(getClass().getResource("xxx.fxml"));
</syntaxhighlight>
* getClass() - 현재 클래스 리턴
* getResource() - 클래스가 위치하는 곳에서 상대 경로로 리소스의 URL을 리턴
* load() - FXML 파일을 로딩
<syntaxhighlight lang="java">
FXMLLoader loader = new FXMLLoader(getClass().getResource("xxx.fxml"));
Parent root = (Parent)loader.load();
</syntaxhighlight>
* load() - Parent 타입을 리턴함. 이것은 FXML 파일에서의 루트 태그로 선언된 컨테이너임
* 만을 루트 태그가 <HBox> 라면 다음과 같이 작성해도 됨
<syntaxhighlight lang="java">
HBox hbox = (HBox) FXMLLoader.load(getClass().getResource("xxx.fxml"));
</syntaxhighlight>
* 예제 코드
<syntaxhighlight lang="java">
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);
    }
}
</syntaxhighlight>
=== JavaFX Scene Builder ===
[[file:CP-SceneBuilder-1.png | thumb | Scene Builder로 root.fxml 띄움]]
* Scene Builder 다운로드 - https://gluonhq.com/products/scene-builder/
* [https://marketplace.visualstudio.com/items?itemName=bilalekrem.scenebuilderextension SceneBuilder extension for Visual Studio Code]
** vscode에서 Ctrl-Shift-P를 눌러 "Configure Scene Builder path"를 실행한다.
*** 경로를 다음과 같이 지정한다 : C:\Users\profs\AppData\Local\SceneBuilder 폴더의 SceneBuilder.exe 지정
* Scene Builder 띄우는 방법
** fxml 파일을 선택한다.
** vscode에서 Ctrl-Shift-P를 누른다.
== JavaFX 컨테이너 ==
<table2 class=wikitable head=top sep=bar align=ll>
컨테이너  | 설명
AnchorPane | 컨트롤을 좌표로 배치하는 레이아웃
BorderPane | 위, 아래, 오른쪽, 왼쪽, 중앙에 컨트롤을 배치하는 레이아웃
FlowPane  | 행으로 배치하되 공간이 부족하면 새로운 행에 배치하는 레이아웃
GridPane  | 그리드로 배치하되 셀의 크기가 고정적이지 않은 레이아웃
StackPane  | 컨트롤을 겹쳐서 배치하는 레이아웃
TilePane  | 그리드로 배치하되 고정된 셀의 크기를 갖는 레이아웃
HBox      | 수평으로 배치하는 레이아웃
VBox      | 수직으로 배치하는 레이아웃
</table2>
=== AnchorPane 컨테이너 ===
[[file:CP-17.4.1-1.png]]
* AnchorPane에서 사용할 수 있는 주요 설정
<table2 class=wikitable head=top sep=bar align=lll>
태그 및 속성 | 설명            | 적용
PrefWidth    | 폭을 설정      | AnchorPane
PrefHeight  | 높이를 설정    | AnchorPane
layoutX      | 컨트롤의 X 좌표 | 컨트롤
layoutY      | 컨트롤의 Y 좌표 | 컨트롤
<children>  | 컨트롤을 포함  | AnchorPane
</table2>
[[file:CP-17.4.1-2.png | thumb | AnchorPane (Scene Builder)]]
<syntaxhighlight lang="java">
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns:fx="http://javafx.com/fxml" prefHeight="150.0" prefWidth="300.0">
  <children>
      <Label layoutX="42.0" layoutY="28.0" text="아이디" />
      <Label layoutX="42.0" layoutY="66.0" text="패스워드" />
      <TextField layoutX="120.0" layoutY="24.0" />
      <PasswordField layoutX="120.0" layoutY="62.0" />
      <Button layoutX="97.0" layoutY="106.0" text="로그인" />
      <Button layoutX="164.0" layoutY="106.0" text="취소" />
  </children>
</AnchorPane>
</syntaxhighlight>
=== HBox와 VBox 컨테이너 ===
<table2 class=wikitable head=top sep=bar align=lll>
태그 및 속성 | 설명                        | 적용
prefWidth    | 폭을 설정                    | HBox, VBox
prefHeight  | 높이를 설정                  | HBox, VBox
alignment    | 컨트롤의 정렬을 설정        | HBox, VBox
spacing      | 컨트롤의 간격을 설정        | HBox, VBox
fillWidth    | 컨트롤의 폭 확장 여부 설정  | VBox
fillHeight  | 컨트롤의 높이 확장 여부 설정 | HBox
<children>  | 컨트롤을 포함                | HBox, VBox
<HBox.hgrow><br/>{{sp2}}<Priority fx:constant="ALWAYS"/><br/></HBox.hgrow> | HBox의 남은 폭을 채움  | 컨트롤
<VBox.vgrow><br/>{{sp2}}<Priority fx:constant="ALWAYS"/><br/></VBox.vgrow> | VBox의 남을 높이를 채움 | 컨트롤
</table2>
* root2.fxml
** 이 패키지의 하위 폴더로 <code>images</code>를 만들고 여기에 javafx.png 파일을 복사해 넣어둔다. [[file:javafx.png | thumb | javafx.png]]
[[file:CP-17.4.3-project.png | thumb | images 폴더]]
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.Double?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.Priority?>
<VBox xmlns:fx="http://javafx.com/fxml">
  <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
  </padding>
  <children>
      <ImageView fitWidth="200.0" preserveRatio="true"> <!-- 그림의 비율에 맞게 높이를 설정 -->
        <image>
          <Image url="@images/javafx.png" /> <!-- 현재 경로 기준으로 상대경로로 파일 지정 -->
        </image>
      </ImageView>
      <HBox alignment="CENTER" spacing="20.0">
        <children>
            <Button text="이전" />
            <Button text="다음">
              <HBox.hgrow><Priority fx:constant="ALWAYS"/></HBox.hgrow> <!-- 오른쪽 남은 공간을 버튼이 모두 채우도록 설정 -->
              <maxWidth><Double fx:constant="MAX_VALUE"/></maxWidth> <!-- 버튼의 폭을 자동으로 확장하기 위해 설정-->
            </Button>
        </children>
        <VBox.margin>
          <Insets top="10.0" />
        </VBox.margin>
      </HBox>
  </children>
</VBox>
</syntaxhighlight>
* 예제
[[file:CP-17.4.3-p874.png | thumb | 실행 결과]]
<syntaxhighlight lang="java">
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("root2.fxml"));
        Scene scene = new Scene(root);
        primaryStage.setTitle("AppMain");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
   
    public static void main(String[] args) {
        launch(args);
    }
}
</syntaxhighlight>
=== BorderPane 컨테이너 ===
* top, bottom, left, right, center 셀에 컨트롤을 배치하는 컨테이너
* 다른 컨테이너를 배치할 수도 있음
* 각 셀에는 '''하나'''의 컨트롤 또는 컨테이너만 배치할 수 있음
[[file:CP-17.4.4-1.png | 250px]]
<table2 class=wikitable head=top sep=bar align=lll>
태그 및 속성 | 설명                          | 적용
prefWidth    | 폭을 설정                      | BorderPane
prefHeight  | 높이를 설정                    | BorderPane
<top>        | top에 배치될 컨트롤을 포함    | BorderPane
<bottom>    | bottom에 배치될 컨트롤을 포함  | BorderPane
<right>      | right에 배치될 컨트롤을 포함  | BorderPane
<left>      | left에 배치될 컨트롤을 포함    | BorderPane
<nowiki><center></nowiki>    | center에 배치될 컨트롤을 포함  | BorderPane
</table2>
[[file:CP-17.4.4-실행결과.png | thumb | 실행 결과]]
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane prefHeight="200.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
  <top>
      <ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
        <items>
          <Button text="Button" />
          <Button text="Button" />
        </items>
      </ToolBar>
  </top>
  <center>
      <TextArea /> <!-- left와 right까지 확장-->
  </center>
  <bottom>
      <BorderPane>
        <center>
          <TextField /> <!-- top, bottom, left까지 확장-->
        </center>
        <right>
            <Button text="Button" />
        </right>
      </BorderPane>
  </bottom>
</BorderPane>
</syntaxhighlight>
=== FlowPane 컨테이너 ===
<table2 class=wikitable head=top sep=bar align=lll>
태그 및 속성 | 설명                      | 적용
prefWidth    | 폭을 설정                | FlowPane
prefHeight  | 높이를 설정              | FlowPane
hgap        | 컨트롤의 수평 간격을 설정 | FlowPane
vgap        | 컨트롤의 수직 간격을 설정 | FlowPane
<children>  | 컨트롤을 포함            |  FlowPane
</table2>
[[file:CP-17.4.5-실행결과.png | thumb | 실행 결과]]
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.FlowPane?>
<FlowPane prefHeight="70.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
  <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
  </padding>
  <children>
      <Button text="Button" />
      <Button text="Button" />
      <Button text="Button" />
      <Button text="Button" />
      <Button text="Button" />
      <Button text="Button" />
  </children>
</FlowPane>
</syntaxhighlight>
=== TilePane 컨테이너 ===
* 그리드로 컨트롤을 배치하되 고정된 셀(타일) 크기를 갖는 컨테이너
* 오른쪽에 배치할 공간이 부족하면 새로운 행에 컨트롤을 배치함
[[file:CP-17.4.6-TilePane.png | 400px]]
<table2 class=wikitable head=top sep=bar align=lll>
태그 및 속성  | 설명              | 적용
prefWidth      | 폭을 설정          | TilePane
prefHeight    | 높이를 설정        | TilePane
prefTileWidth  | 타일의 폭을 설정  | TilePane
prefTileHeight | 타일의 높이를 설정 | TilePane
<children>    | 컨트롤을 포함      | TilePane
</table2>
<gallery>
file:fruit1.jpg | fruit1.jpg
file:fruit2.jpg | fruit2.jpg
file:fruit3.jpg | fruit3.jpg
file:fruit4.jpg | fruit4.jpg
file:fruit5.jpg | fruit5.jpg
</gallery>
[[file:CP-17.4.6-실행결과.png | thumb | 실행 결과]]
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.TilePane?>
<TilePane xmlns:fx="http://javafx.com/fxml" prefHeight="100.0" prefWidth="100.0">
  <children>
      <ImageView>
        <image><Image url="@images/fruit1.jpg" /></image>
      </ImageView>
      <ImageView>
        <image><Image url="@images/fruit2.jpg" /></image>
      </ImageView>
      <ImageView>
        <image><Image url="@images/fruit3.jpg" /></image>
      </ImageView>
      <ImageView>
        <image><Image url="@images/fruit4.jpg" /></image>
      </ImageView>
      <ImageView>
        <image><Image url="@images/fruit5.jpg" /></image>
      </ImageView>
  </children>
</TilePane>
</syntaxhighlight>
=== GridPane 컨테이너 ===
<table2 class=wikitable head=top sep=bar align=lll>
태그 및 속성        | 설명                                | 적용
prefWidth            | 폭을 설정                            | GridPane
prefHeight          | 놎이를 설정                          | GridPane
hgap                | 수평 컨트롤 간격을 설정              | GridPane
vgap                | 수직 컨트롤 간격을 설정              | GridPane
<children>          | 컨트롤을 포함                        | GridPane
GridPane.rowIndex    | 컨트롤이 위치하는 행 인덱스를 설정  | 컨트롤
GridPane.columnIndex | 컨트롤이 위치하는 컬럼 인덱스를 설정 | 컨트롤
GridPane.rowSpan    | 행 병합 수를 설정                    | 컨트롤
GridPane.columnSpan  | 컬럼 병합 수를 설정                  | 컨트롤
GridPane.hgrow      | 수평 빈 공간을 채우기로 설정        | 컨트롤
GridPane.vgrow      | 수직 빈 공간을 채우기로 설정        | 컨트롤
GridPane.halignment  | 컨트롤의 수평 정렬을 설정            | 컨트롤
GridPane.valignment  | 컨트롤의 수직 정렬을 설정            | 컨트롤
</table2>
[[file:CP-17.4.7-실행결과.png | thumb | 실행 결과]]
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane xmlns:fx="http://javafx.com/fxml" prefWidth="300.0" hgap="10.0" vgap="10.0">
  <padding>
      <Insets topRightBottomLeft="10.0" />
  </padding>
  <children>
      <Label text="아이디" GridPane.rowIndex="0" GridPane.columnIndex="0" />
      <TextField GridPane.rowIndex="0" GridPane.columnIndex="1"
                GridPane.hgrow="ALWAYS" /> <!-- 오른쪽 빈공간까지 확장 -->
      <Label text="패스워드" GridPane.rowIndex="1" GridPane.columnIndex="0" />
      <TextField GridPane.columnIndex="1" GridPane.rowIndex="1"
                GridPane.hgrow="ALWAYS" /> <!-- 오른쪽 빈공간까지 확장 -->
      <HBox GridPane.rowIndex="2" GridPane.columnIndex="0"
                GridPane.columnSpan="2" GridPane.hgrow="ALWAYS"
                alignment="CENTER" spacing="20.0" >  <!-- 컬럼 2개 병합 -->
        <children>
            <Button text="로그인" />
            <Button text="취소" />
        </children>
      </HBox>
  </children>
</GridPane>
</syntaxhighlight>
=== StackPane 컨테이너 ===
* 컨트롤을 겹쳐 배치하는 컨테이너
* 카드 레이아웃(Card Layout)이라고도 함
* 만약 위에 있는 컨트롤이 투명하다면 밑에 있는 컨트롤이 겹쳐 보임
<gallery>
file:duke.jpg | duke.jpg
file:snow.jpg | snow.jpg
</gallery>
[[file:CP-17.4.8-실행결과.png | thumb | 실행 결과]]
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.layout.StackPane?>
<StackPane  xmlns:fx="http://javafx.com/fxml" prefHeight="300.0" prefWidth="500.0">
  <children>
      <ImageView fitHeight="300.0" fitWidth="500.0">
        <image>
            <Image url="@images/snow.jpg" />
        </image>
      </ImageView>
      <ImageView preserveRatio="true">
        <image>
            <Image url="@images/duke.jpg" />
        </image>
      </ImageView>
  </children>
</StackPane>
</syntaxhighlight>
== JavaFX 이벤트 처리 ==
== JavaFX 속성 감시와 바인딩 ==
== JavaFX 컨트롤 ==
== JavaFX 컨트롤 ==


Line 851: Line 6:


== JavaFX 다이얼로그(Dialog) ==
== JavaFX 다이얼로그(Dialog) ==
== JavaFX CSS 스타일 ==
== JavaFX 스레드 동시성 ==
== 화면 이동과 애니메이션 ==

Revision as of 21:06, 21 July 2022

JavaFX 컨트롤

JavaFX 메뉴바(MenuBar)와 툴바(Toolbar)

JavaFX 다이얼로그(Dialog)