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

From DISLab
Jump to navigation Jump to search
 
(15 intermediate revisions by the same user not shown)
Line 1: Line 1:
== JavaFX 개요 ==
== 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>
* ButtonBase를 상속하는 하위 컨트롤
프로그램적 레이아웃 자바 코드 | FXML 레이아웃 태그
* Button, CheckBox, RadioButton, ToggleButton, Hyperlink 등
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>
* p.896 그림 참조
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) 선언 ====
* Button
<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">
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<Button text="아이콘버튼">
 
  <graphic>
<?import javafx.scene.layout.HBox?>
      <ImageView>
<?import javafx.scene.control.*?>
        <Image url="@iamges/history_view.gif"/>
 
      </ImageView>
<루트 컨테이너 xmlns:fx="http://javafx.com/fxml">
  </graphic>
...
</Button>
</루트 컨테이너>
</syntaxhighlight>
</syntaxhighlight>


* import를 제대로 하지 않으면 not a valid type 메시지와 함께 javafx.fxml.LoadException 발생
* selected 속성 - CheckBox, RadioButton, ToggleButton : 선택, 미선택 (selected가 true 혹은 false)
 
==== 태그(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 lang="xml">
<태그이름 속성명="" 속성명='값'> ... </태그이름>
<CheckBox text="라벨1" userData="값1"/>
<CheckBox text="라벨2" userData="값2" selected="true"/>
</syntaxhighlight>
</syntaxhighlight>


* 속성명은 Setter 메소드 명이 옴
* toggleGroup 속성 - RadioButton, ToggleButton : 하나의 그룹으로 묶이고, 그룹 내에서는 하나만 선택
* 모든 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">
<syntaxhighlight lang="xml">
<클래스 fx:value="값" />
<fx:deine>
</syntaxhighlight>
  <ToggleGroup fx:=id="groupName"/>
 
</fx:define>
<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 =====
<RadioButton text="라벨1" userData="값1" toggleGroup="$groupName" />
*클래스에 정의된 상수값을 얻고 싶을 경우
<RadioButton text="라벨2" userData="값2" toggleGroup="$groupName" selected="true" />
<syntaxhighlight lang="xml">
<클래스 fx:constant="상수" />
</syntaxhighlight>
</syntaxhighlight>


<table2 class=wikitable head=top sep=bar align=ll>
* ActionEvent 발생 - CheckBox, RadioButton, ToggleButton : 컨트롤을 사용하가 클릭할 경우
기본 코드  | 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">
<syntaxhighlight lang="xml">
<클래스 fx:factory="정적메소드" />
<CheckBox ... onAction="#handleChkAction"/>
</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>
</syntaxhighlight>
* getClass() - 현재 클래스 리턴
* getResource() - 클래스가 위치하는 곳에서 상대 경로로 리소스의 URL을 리턴
* load() - FXML 파일을 로딩


* RadioButton, ToggleButton 그룹 내에서 선택 변경을 감시하고 싶다면
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
FXMLLoader loader = new FXMLLoader(getClass().getResource("xxx.fxml"));
groupName.selectedToggleProperty().addListener(new ChangeListener<Toggle> {
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
     @Override
     public void start(Stage primaryStage) throws Exception {
     public void changed(ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle newValue) {
         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>
</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>
[[file:javafx.png | thumb | javafx.png]]
[[file:CP-17.4.3-project.png | thumb | images 폴더]]
* root2.fxml
** 이 패키지의 하위 폴더로 <code>images</code>를 만들고 여기에 javafx.png 파일을 복사해 넣어둔다.
<syntaxhighlight lang="xml">
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>


<?import java.lang.Double?>
<?import javafx.geometry.Insets?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.Priority?>




<VBox xmlns:fx="http://javafx.com/fxml">
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="ch17.p897.RootController" prefHeight="150.0" prefWidth="420.0">
   <padding>
   <padding>
       <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
       <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
   </padding>
   </padding>


   <children>
   <center>
       <ImageView fitWidth="200.0" preserveRatio="true">
       <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="10">
        <image>
          <Image url="@images/javafx.png" />
        </image>
      </ImageView>
      <HBox alignment="CENTER" spacing="20.0">
         <children>
         <children>
             <Button text="이전" />
             <VBox prefHeight="200.0" prefWidth="100.0" spacing="20.0" alignment="CENTER_LEFT">
            <Button text="다음">
              <children>
              <HBox.hgrow><Priority fx:constant="ALWAYS"/></HBox.hgrow>
                  <CheckBox fx:id="chk1" text="안경" onAction="#handleChkAction" />
              <maxWidth><Double fx:constant="MAX_VALUE"/></maxWidth>
                  <CheckBox fx:id="chk2" text="모자" onAction="#handleChkAction" />
            </Button>
              </children>
        </children>
            </VBox>
        <VBox.margin>
          <Insets top="10.0" />
        </VBox.margin>
      </HBox>
  </children>
</VBox>
</syntaxhighlight>


* 예제
            <ImageView fx:id="checkImageView" fitWidth="200.0" preserveRatio="true">
[[file:CP-17.4.3-p874.png | thumb | 실행 결과]]
              <image><Image url="@images/geek.gif" /></image>
<syntaxhighlight lang="java">
            </ImageView>
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 {
            <Separator orientation="VERTICAL" prefHeight="200.0" />


    @Override
            <VBox prefHeight="200.0" prefWidth="100.0">
    public void start(Stage primaryStage) throws Exception {
              <fx:define>
        Parent root = FXMLLoader.load(getClass().getResource("root2.fxml"));
                  <ToggleGroup fx:id="group" />
        Scene scene = new Scene(root);
              </fx:define>


        primaryStage.setTitle("AppMain");
              <children>
        primaryStage.setScene(scene);
                  <RadioButton fx:id="rad1" text="BubbleChart" userData="BubbleChart" toggleGroup="$group"/>
        primaryStage.show();
                  <RadioButton fx:id="rad2" text="BarChart" userData="BarChart" toggleGroup="$group" selected="true" />
    }
                  <RadioButton fx:id="rad3" text="AreaChart" userData="AreaChart" toggleGroup="$group"/>
   
              </children>
    public static void main(String[] args) {
            </VBox>
        launch(args);
    }
}
</syntaxhighlight>


=== BorderPane 컨테이너 ===
            <ImageView fx:id="radioImageView" fitHeight="100.0" preserveRatio="true">
<table2 class=wikitable head=top sep=bar align=lll>
              <image>
태그 및 속성 | 설명 | 적용
                  <Image url="@images/BarChart.png" />
              </image>
            </ImageView>
        </children>
      </HBox>
  </center>


</table2>
    <bottom>
      <Button fx:id="btnExit" BorderPane.alignment="CENTER" onAction="#handleBtnExitAction">
        <graphic>
            <ImageView>
              <Image url="@images/exit.png" />
            </ImageView>
        </graphic>
        <BorderPane.margin>
            <Insets top="20.0" />
        </BorderPane.margin>
      </Button>
  </bottom>
</BorderPane>
</syntaxhighlight>


<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
package ch17.p897;


</syntaxhighlight>
import java.net.URL;
import java.util.ResourceBundle;


import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;


=== FlowPane 컨테이너 ===
public class RootController implements Initializable {
<table2 class=wikitable head=top sep=bar align=lll>
    @FXML private CheckBox chk1;
태그 및 속성 | 설명 | 적용
    @FXML private CheckBox chk2;
    @FXML private ImageView checkImageView;
    @FXML private ToggleGroup group;
    @FXML private ImageView radioImageView;
    @FXML private Button btnExit;


</table2>
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        group.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {


<syntaxhighlight lang="java">
            @Override
            public void changed(ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle newValue) {
                Image image = new Image(getClass().getResource("images/" +
                                        newValue.getUserData().toString() + ".png").toString());
                radioImageView.setImage(image);               
            }
           
        });


</syntaxhighlight>
    }


    // CheckBox 이벤트 처리
    public void handleChkAction(ActionEvent e) {
        if (chk1.isSelected() && chk2.isSelected()) {
            checkImageView.setImage(new Image(getClass().getResource("images/geek-glasses-hair.gif").toString()));
        } else if (chk1.isSelected()) {
            checkImageView.setImage(new Image(getClass().getResource("images/geek-glasses.gif").toString()));
        } else if (chk2.isSelected()) {
            checkImageView.setImage(new Image(getClass().getResource("images/geek-hair.gif").toString()));
        } else {
            checkImageView.setImage(new Image(getClass().getResource("images/geek.gif").toString()));
        }
    }


=== TilePane 컨테이너 ===
    // Button 이벤트 처리
<table2 class=wikitable head=top sep=bar align=lll>
    public void handleBtnExitAction(ActionEvent e) {
태그 및 속성 | 설명 | 적용
        Platform.exit();
    }


</table2>
}
</syntaxhighlight>


<syntaxhighlight lang="java">


</syntaxhighlight>
=== 입력 컨트롤 ===




=== GridPane 컨테이너 ===
=== 뷰 컨트롤 ===
<table2 class=wikitable head=top sep=bar align=lll>
태그 및 속성 | 설명 | 적용


</table2>
==== ImageView 컨트롤 ====


<syntaxhighlight lang="java">
==== ListView 컨트롤 ====


</syntaxhighlight>
==== TableView 컨트롤 ====




=== StackPane 컨테이너 ===
=== 미디어 컨트롤 ===
<table2 class=wikitable head=top sep=bar align=lll>
태그 및 속성 | 설명 | 적용


</table2>
==== Slider 컨트롤 ====


<syntaxhighlight lang="java">


</syntaxhighlight>
==== ProgressBar와 ProgressIndicator 컨트롤 ====


== JavaFX 이벤트 처리 ==
=== 차트 컨트롤 ===




== JavaFX 속성 감시와 바인딩 ==


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


== JavaFX 컨트롤 ==
=== MenuBar 컨트롤 ===


=== ToolBar 컨트롤 ===


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




== JavaFX 다이얼로그(Dialog) ==
== JavaFX 다이얼로그(Dialog) ==


=== FileChooser, DirectoryChooser ===


== JavaFX CSS 스타일 ==
=== Popup ===


=== Custom Dialog ===


== JavaFX 스레드 동시성 ==
=== 컨트롤러에서 primaryStage 사용 ===


==== 메인 클래스에 전달하는 방법 ====


== 화면 이동과 애니메이션 ==
==== 컨테이너 또는 컨트롤로부터 얻는 방법 ====

Latest revision as of 15:57, 22 July 2022

JavaFX 컨트롤

버튼 컨트롤

  • ButtonBase를 상속하는 하위 컨트롤
  • Button, CheckBox, RadioButton, ToggleButton, Hyperlink 등
  • p.896 그림 참조
  • Button
<Button text="아이콘버튼">
   <graphic>
      <ImageView>
         <Image url="@iamges/history_view.gif"/>
      </ImageView>
   </graphic>
</Button>
  • selected 속성 - CheckBox, RadioButton, ToggleButton : 선택, 미선택 (selected가 true 혹은 false)
<CheckBox text="라벨1" userData="값1"/>
<CheckBox text="라벨2" userData="값2" selected="true"/>
  • toggleGroup 속성 - RadioButton, ToggleButton : 하나의 그룹으로 묶이고, 그룹 내에서는 하나만 선택
<fx:deine>
   <ToggleGroup fx:=id="groupName"/>
</fx:define>

<RadioButton text="라벨1" userData="값1" toggleGroup="$groupName" />
<RadioButton text="라벨2" userData="값2" toggleGroup="$groupName" selected="true" />
  • ActionEvent 발생 - CheckBox, RadioButton, ToggleButton : 컨트롤을 사용하가 클릭할 경우
<CheckBox ... onAction="#handleChkAction"/>
  • RadioButton, ToggleButton 그룹 내에서 선택 변경을 감시하고 싶다면
groupName.selectedToggleProperty().addListener(new ChangeListener<Toggle> {
    @Override
    public void changed(ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle newValue) {
        ...
    }
});
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>


<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="ch17.p897.RootController" prefHeight="150.0" prefWidth="420.0">
   <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
   </padding>

   <center>
      <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="10">
         <children>
            <VBox prefHeight="200.0" prefWidth="100.0" spacing="20.0" alignment="CENTER_LEFT">
               <children>
                  <CheckBox fx:id="chk1" text="안경" onAction="#handleChkAction" />
                  <CheckBox fx:id="chk2" text="모자" onAction="#handleChkAction" />
               </children>
            </VBox>

            <ImageView fx:id="checkImageView" fitWidth="200.0" preserveRatio="true">
               <image><Image url="@images/geek.gif" /></image>
            </ImageView>

            <Separator orientation="VERTICAL" prefHeight="200.0" />

            <VBox prefHeight="200.0" prefWidth="100.0">
               <fx:define>
                  <ToggleGroup fx:id="group" />
               </fx:define>

               <children>
                  <RadioButton fx:id="rad1" text="BubbleChart" userData="BubbleChart" toggleGroup="$group"/>
                  <RadioButton fx:id="rad2" text="BarChart" userData="BarChart" toggleGroup="$group" selected="true" />
                  <RadioButton fx:id="rad3" text="AreaChart" userData="AreaChart" toggleGroup="$group"/>
               </children>
            </VBox>

            <ImageView fx:id="radioImageView" fitHeight="100.0" preserveRatio="true">
               <image>
                  <Image url="@images/BarChart.png" />
               </image>
            </ImageView>
         </children>
      </HBox>
   </center>

    <bottom>
      <Button fx:id="btnExit" BorderPane.alignment="CENTER" onAction="#handleBtnExitAction">
         <graphic>
            <ImageView>
               <Image url="@images/exit.png" />
            </ImageView>
         </graphic>
         <BorderPane.margin>
            <Insets top="20.0" />
         </BorderPane.margin>
      </Button>
   </bottom>
</BorderPane>
package ch17.p897;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

public class RootController implements Initializable {
    @FXML private CheckBox chk1;
    @FXML private CheckBox chk2;
    @FXML private ImageView checkImageView;
    @FXML private ToggleGroup group;
    @FXML private ImageView radioImageView;
    @FXML private Button btnExit;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        group.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {

            @Override
            public void changed(ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle newValue) {
                Image image = new Image(getClass().getResource("images/" +
                                        newValue.getUserData().toString() + ".png").toString());
                radioImageView.setImage(image);                
            }
            
        });

    }

    // CheckBox 이벤트 처리
    public void handleChkAction(ActionEvent e) {
        if (chk1.isSelected() && chk2.isSelected()) {
            checkImageView.setImage(new Image(getClass().getResource("images/geek-glasses-hair.gif").toString()));
        } else if (chk1.isSelected()) {
            checkImageView.setImage(new Image(getClass().getResource("images/geek-glasses.gif").toString()));
        } else if (chk2.isSelected()) {
            checkImageView.setImage(new Image(getClass().getResource("images/geek-hair.gif").toString()));
        } else {
            checkImageView.setImage(new Image(getClass().getResource("images/geek.gif").toString()));
        }
    }

    // Button 이벤트 처리
    public void handleBtnExitAction(ActionEvent e) {
        Platform.exit();
    }

}


입력 컨트롤

뷰 컨트롤

ImageView 컨트롤

ListView 컨트롤

TableView 컨트롤

미디어 컨트롤

Slider 컨트롤

ProgressBar와 ProgressIndicator 컨트롤

차트 컨트롤

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

MenuBar 컨트롤

ToolBar 컨트롤

JavaFX 다이얼로그(Dialog)

FileChooser, DirectoryChooser

Popup

Custom Dialog

컨트롤러에서 primaryStage 사용

메인 클래스에 전달하는 방법

컨테이너 또는 컨트롤로부터 얻는 방법