2025/12/08

JavaFX 04 Control

JavaFX UI control 物件是在 javafx.scene.control.* package 裡面

以功能來區分,可以這樣分類

功能 代表 Control
按鈕與開關 Button, ToggleButton, CheckBox, RadioButton, Hyperlink, MenuButton, SplitMenuButton
文字輸入 TextField, TextArea, PasswordField
選擇器與下拉 ChoiceBox, ComboBox, ColorPicker, DatePicker, Spinner
清單/樹格顯示 ListView, TreeView, TableView
對話與頁籤 TabPane, Dialog, MenuBar, Alert, TextInputDialog, ChoiceDialog, ContextMenu
滾動/分隔容器 ScrollPane, SplitPane, Separator
工具列與分頁 ToolBar, ButtonBar, Pagination, Accordion

Button

package javafx;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ControlButton extends Application {
    @Override
    public void start(Stage primaryStage) {
        // 1. Button
        Button button = new Button("Standard Button");
        button.setOnAction(e -> System.out.println("Button clicked"));

        // 2. ToggleButton
        ToggleButton toggleButton = new ToggleButton("Toggle Me");
        toggleButton.setOnAction(e -> System.out.println("Toggle is " + (toggleButton.isSelected() ? "ON" : "OFF")));

        // 3. CheckBox
        CheckBox checkBox = new CheckBox("Accept Terms");
        checkBox.setOnAction(e -> System.out.println("Checkbox: " + checkBox.isSelected()));

        // 4. RadioButton (in a ToggleGroup)
        RadioButton option1 = new RadioButton("Option A");
        RadioButton option2 = new RadioButton("Option B");
        ToggleGroup radioGroup = new ToggleGroup();
        option1.setToggleGroup(radioGroup);
        option2.setToggleGroup(radioGroup);

        option1.setOnAction(e -> System.out.println("Radio selected: Option A"));
        option2.setOnAction(e -> System.out.println("Radio selected: Option B"));

        // 5. Hyperlink
        Hyperlink link = new Hyperlink("Open Website");
        link.setOnAction(e -> System.out.println("Hyperlink clicked"));

        // 6. MenuButton
        MenuItem item1 = new MenuItem("Menu A");
        MenuItem item2 = new MenuItem("Menu B");
        MenuButton menuButton = new MenuButton("Choose Menu", null, item1, item2);
        item1.setOnAction(e -> System.out.println("Menu A clicked"));
        item2.setOnAction(e -> System.out.println("Menu B clicked"));

        // 7. SplitMenuButton
        MenuItem subItem1 = new MenuItem("Sub A");
        MenuItem subItem2 = new MenuItem("Sub B");
        SplitMenuButton splitMenuButton = new SplitMenuButton(subItem1, subItem2);
        splitMenuButton.setText("Action + Menu");
        splitMenuButton.setOnAction(e -> System.out.println("Main action clicked"));
        subItem1.setOnAction(e -> System.out.println("Sub A clicked"));
        subItem2.setOnAction(e -> System.out.println("Sub B clicked"));

        // Layout
        VBox root = new VBox(10,
                button,
                toggleButton,
                checkBox,
                option1,
                option2,
                link,
                menuButton,
                splitMenuButton
        );
        root.setPadding(new Insets(20));
        root.setAlignment(Pos.TOP_LEFT);

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setTitle("JavaFX Buttons & Switches Sample");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Input

package javafx;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ControlInput extends Application {

    @Override
    public void start(Stage primaryStage) {
        // 1. TextField
        TextField textField = new TextField();
        textField.setPromptText("Enter your name");

        // 2. TextArea
        TextArea textArea = new TextArea();
        textArea.setPromptText("Enter your comments");
        textArea.setPrefRowCount(4);

        // 3. PasswordField
        PasswordField passwordField = new PasswordField();
        passwordField.setPromptText("Enter password");

        // Button to show entered data
        Button submitButton = new Button("Submit");
        submitButton.setOnAction(e -> {
            System.out.println("Name: " + textField.getText());
            System.out.println("Comments: " + textArea.getText());
            System.out.println("Password: " + passwordField.getText());
        });

        // Layout
        VBox root = new VBox(10,
                new Label("Name:"), textField,
                new Label("Comments:"), textArea,
                new Label("Password:"), passwordField,
                submitButton
        );
        root.setPadding(new Insets(20));
        root.setAlignment(Pos.TOP_LEFT);

        Scene scene = new Scene(root, 400, 300);
        primaryStage.setTitle("JavaFX Text Input Sample");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Selector

package javafx;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.StringConverter;

public class ControlSelector extends Application {
    @Override
    public void start(Stage primaryStage) {
        // 1. ChoiceBox
        ChoiceBox<String> choiceBox = new ChoiceBox<>();
        choiceBox.getItems().addAll("Apple", "Banana", "Cherry");
        choiceBox.setValue("Banana");

        // 2. ComboBox
        ComboBox<String> comboBox = new ComboBox<>();
        comboBox.getItems().addAll("Red", "Green", "Blue");
        comboBox.setEditable(true);
        comboBox.setValue("Green");

        // 3. ColorPicker
        ColorPicker colorPicker = new ColorPicker(Color.CORNFLOWERBLUE);

        // 4. DatePicker
        DatePicker datePicker = new DatePicker();

        // 5. Spinner (Integer, 0~10)
        Spinner<Integer> spinner = new Spinner<>(0, 10, 5);
        spinner.setEditable(true);

        // Button to show selected values
        Button showButton = new Button("Show Values");
        showButton.setOnAction(e -> {
            System.out.println("ChoiceBox: " + choiceBox.getValue());
            System.out.println("ComboBox: " + comboBox.getValue());
            System.out.println("ColorPicker: " + colorPicker.getValue());
            System.out.println("DatePicker: " + datePicker.getValue());
            System.out.println("Spinner: " + spinner.getValue());
        });

        // Layout
        VBox root = new VBox(10,
                new Label("ChoiceBox:"), choiceBox,
                new Label("ComboBox:"), comboBox,
                new Label("ColorPicker:"), colorPicker,
                new Label("DatePicker:"), datePicker,
                new Label("Spinner:"), spinner,
                showButton
        );
        root.setPadding(new Insets(20));
        root.setAlignment(Pos.TOP_LEFT);

        Scene scene = new Scene(root, 400, 450);
        primaryStage.setTitle("JavaFX Selectors Sample");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

List

package javafx;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ControlList extends Application {

    @Override
    public void start(Stage primaryStage) {
        // 1. ListView
        ListView<String> listView = new ListView<>();
        listView.getItems().addAll("Item A", "Item B", "Item C");

        // 2. TreeView
        TreeItem<String> rootNode = new TreeItem<>("Root");
        rootNode.setExpanded(true);
        rootNode.getChildren().addAll(
                new TreeItem<>("Node 1"),
                new TreeItem<>("Node 2"),
                new TreeItem<>("Node 3")
        );
        TreeView<String> treeView = new TreeView<>(rootNode);

        // 3. TableView
        TableView<Person> tableView = new TableView<>();
        TableColumn<Person, String> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(data -> data.getValue().nameProperty());

        TableColumn<Person, String> emailCol = new TableColumn<>("Email");
        emailCol.setCellValueFactory(data -> data.getValue().emailProperty());

        tableView.getColumns().addAll(nameCol, emailCol);
        tableView.getItems().addAll(
                new Person("Alice", "alice@example.com"),
                new Person("Bob", "bob@example.com"),
                new Person("Carol", "carol@example.com")
        );

        // Layout
        VBox root = new VBox(10,
                new Label("ListView:"), listView,
                new Label("TreeView:"), treeView,
                new Label("TableView:"), tableView
        );
        root.setPrefWidth(400);
        root.setPrefHeight(600);
        Scene scene = new Scene(root, 500, 600);
        primaryStage.setTitle("JavaFX Control List Sample");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    // Inner class for TableView row
    public static class Person {
        private final SimpleStringProperty name;
        private final SimpleStringProperty email;

        public Person(String name, String email) {
            this.name = new SimpleStringProperty(name);
            this.email = new SimpleStringProperty(email);
        }

        public SimpleStringProperty nameProperty() { return name; }
        public SimpleStringProperty emailProperty() { return email; }
    }

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

Dialog

package javafx;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class ControlDialog extends Application {

    @Override
    public void start(Stage primaryStage) {
        // === MenuBar ===
        MenuBar menuBar = new MenuBar();
        Menu fileMenu = new Menu("File");
        MenuItem showAlert = new MenuItem("Show Alert");
        MenuItem showInput = new MenuItem("Show Input Dialog");
        MenuItem showChoice = new MenuItem("Show Choice Dialog");
        fileMenu.getItems().addAll(showAlert, showInput, showChoice);
        menuBar.getMenus().addAll(fileMenu);

        // === TabPane ===
        TabPane tabPane = new TabPane();
        Tab tab1 = new Tab("Home", new Label("This is the Home tab."));
        Tab tab2 = new Tab("Settings", new Label("This is the Settings tab."));
        tabPane.getTabs().addAll(tab1, tab2);

        // === ContextMenu ===
        ContextMenu contextMenu = new ContextMenu();
        MenuItem customDialog = new MenuItem("Open Custom Dialog");
        contextMenu.getItems().add(customDialog);

        Label contextTarget = new Label("Right-click me for ContextMenu");
        contextTarget.setOnMousePressed(e -> {
            if (e.getButton() == MouseButton.SECONDARY) {
                contextMenu.show(contextTarget, e.getScreenX(), e.getScreenY());
            }
        });

        // === Action: Alert ===
        showAlert.setOnAction(e -> {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.setTitle("Information");
            alert.setHeaderText("This is an alert.");
            alert.setContentText("Everything is working fine.");
            alert.showAndWait();
        });

        // === Action: TextInputDialog ===
        showInput.setOnAction(e -> {
            TextInputDialog inputDialog = new TextInputDialog("Default");
            inputDialog.setTitle("Input Dialog");
            inputDialog.setHeaderText("Please enter your name:");
            Optional<String> result = inputDialog.showAndWait();
            result.ifPresent(name -> System.out.println("Entered: " + name));
        });

        // === Action: ChoiceDialog ===
        showChoice.setOnAction(e -> {
            List<String> choices = Arrays.asList("Dog", "Cat", "Bird");
            ChoiceDialog<String> choiceDialog = new ChoiceDialog<>("Cat", choices);
            choiceDialog.setTitle("Choice Dialog");
            choiceDialog.setHeaderText("Choose your favorite animal:");
            Optional<String> result = choiceDialog.showAndWait();
            result.ifPresent(choice -> System.out.println("Selected: " + choice));
        });

        // === Action: Custom Dialog ===
        customDialog.setOnAction(e -> {
            Dialog<String> dialog = new Dialog<>();
            dialog.setTitle("Custom Dialog");
            dialog.setHeaderText("This is a custom dialog.");

            ButtonType okButton = new ButtonType("OK", ButtonBar.ButtonData.OK_DONE);
            dialog.getDialogPane().getButtonTypes().addAll(okButton, ButtonType.CANCEL);

            dialog.setContentText("This dialog returns a string.");
            dialog.setResultConverter(dialogButton -> {
                if (dialogButton == okButton) {
                    return "Confirmed";
                }
                return null;
            });

            Optional<String> result = dialog.showAndWait();
            result.ifPresent(r -> System.out.println("Dialog result: " + r));
        });

        // === Layout ===
        StackPane centerPane = new StackPane(contextTarget);
        BorderPane root = new BorderPane();
        root.setTop(menuBar);
        root.setCenter(tabPane);
        root.setBottom(centerPane);

        Scene scene = new Scene(root, 500, 400);
        primaryStage.setTitle("JavaFX ControlDialog Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Separator

package javafx;

import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class ControlSeparator extends Application {

    @Override
    public void start(Stage primaryStage) {
        // === ScrollPane: 可滾動內容 ===
        VBox scrollContent = new VBox(10);
        for (int i = 1; i <= 30; i++) {
            scrollContent.getChildren().add(new Label("Scrollable Item " + i));
        }
        ScrollPane scrollPane = new ScrollPane(scrollContent);
        scrollPane.setFitToWidth(true);

        // === Separator ===
        Separator horizontalSeparator = new Separator();
        Separator verticalSeparator = new Separator(Orientation.VERTICAL);

        // === SplitPane: 左右分隔區域 ===
        VBox leftPane = new VBox(5, new Label("Left Panel"), new Button("Left Action"));
        VBox rightPane = new VBox(5, new Label("Right Panel"), new Button("Right Action"));
        SplitPane splitPane = new SplitPane(leftPane, verticalSeparator, rightPane);
        splitPane.setDividerPositions(0.45f, 0.55f);

        // === 總佈局:ScrollPane 在上,分隔線,SplitPane 在下 ===
        VBox root = new VBox(10);
        root.getChildren().addAll(
                new Label("ScrollPane Area:"),
                scrollPane,
                horizontalSeparator,
                new Label("SplitPane Area:"),
                splitPane
        );
        root.setPrefHeight(500);

        Scene scene = new Scene(root, 600, 500);
        primaryStage.setTitle("JavaFX ControlSeparator Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Tool

package javafx;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class ControlTool extends Application {

    @Override
    public void start(Stage primaryStage) {
        // === ToolBar ===
        ToolBar toolBar = new ToolBar(
                new Button("New"),
                new Button("Open"),
                new Button("Save")
        );

        // === ButtonBar ===
        ButtonBar buttonBar = new ButtonBar();
        Button okBtn = new Button("OK");
        Button cancelBtn = new Button("Cancel");
        ButtonBar.setButtonData(okBtn, ButtonBar.ButtonData.OK_DONE);
        ButtonBar.setButtonData(cancelBtn, ButtonBar.ButtonData.CANCEL_CLOSE);
        buttonBar.getButtons().addAll(okBtn, cancelBtn);

        // === Pagination (5 pages) ===
        Pagination pagination = new Pagination(5, 0);
        pagination.setPageFactory(pageIndex -> {
            Label pageLabel = new Label("Page " + (pageIndex + 1));
            pageLabel.setStyle("-fx-font-size: 18px;");
            StackPane pageContent = new StackPane(pageLabel);
            pageContent.setPrefHeight(100);
            return pageContent;
        });

        // === Accordion ===
        TitledPane pane1 = new TitledPane("Section 1", new Label("Content of Section 1"));
        TitledPane pane2 = new TitledPane("Section 2", new Label("Content of Section 2"));
        Accordion accordion = new Accordion(pane1, pane2);

        // === Layout ===
        VBox root = new VBox(15);
        root.setPadding(new Insets(15));
        root.getChildren().addAll(
                toolBar,
                new Label("Accordion:"), accordion,
                new Label("Pagination:"), pagination,
                new Label("ButtonBar:"), buttonBar
        );

        Scene scene = new Scene(root, 500, 400);
        primaryStage.setTitle("JavaFX ControlTool Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

2025/12/01

JavaFX 03 Layout

JavaFX Layout 在 javafx.layout package 裡面,有以下類別 HBox, VBox, Border Pane, Stack Pane, Text Flow, Anchor Pane, Title Pane, Grid Pane, Flow Panel,Pane 類別是所有類別的母類別。

create a layout

建立 layout 有以下步驟

  1. create node

  2. 以 layout 的類別產生 instance

  3. 設定 layout 屬性

  4. 將 nodes 加入 layout

create nodes

ex:

//Creating a text field 
TextField textField = new TextField();       

//Creating the play button 
Button playButton = new Button("Play");       

//Creating the stop button 
Button stopButton = new Button("stop");

產生 layout instance

HBox hbox = new HBox();

設定layout屬性

hbox.setSpacing(10);

將 nodes 加入 layout

//Creating a Group object  
hbox.getChildren().addAll(textField, playButton, stopButton);

Layouts

類別 抽象基底類別 說明
Region 抽象基底類別 支援背景、邊框、padding、最小/最大尺寸等,不直接用於排版。
Layout 類別 排版方式 特性 / 用途
Pane 手動設定每個子節點的位置 最基本容器,無自動排版功能。需搭配 setLayoutX/Y() 使用。
HBox 水平排版 將子節點由左至右排列,可設定間距、對齊與每個節點的伸縮比例。
VBox 垂直排版 將子節點由上至下排列,支援間距、對齊、節點高度調整等。
StackPane 子節點重疊排版 最後加入的節點在最上層,適合建立背景圖層或合成式 UI。
BorderPane 五區域:Top / Bottom / Left / Right / Center 適合標準應用主畫面分區(類似 HTML 的 frameset)。分成 Top/Bottom/Left/Right/Center 五個區域
GridPane 表格樣式的列/欄排列 可自由定義 row/column spans,類似 HTML table。
FlowPane 水平/垂直「流動」排列 當空間不夠時,自動換行/換列(類似 CSS flex-wrap: wrap)。
TilePane 固定格大小的網格排列 每個子節點占用相同大小的 tile 區塊,整齊對齊適合 icon 排列等用途。
AnchorPane 錨定(Anchor)子節點邊距 可將節點固定在上下左右的邊界距離,類似 CSS 的 absolute 定位。
ScrollPane 提供可滾動內容區塊 包含一個子節點,支援垂直與水平捲軸。

另外在 javafx.scene.control.TabPane,雖然名稱是 TabPane,但並不是一種 layout,而是 control 元件,TabPane 可建立一個多頁籤的畫面,每一個頁籤,都能容納一種內容節點,通常是 Pane

Example

以下實例,是利用 TabPan 製作這些 Pane layout 的 sample

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class LayoutExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        TabPane tabPane = new TabPane();

        tabPane.getTabs().add(tab("Pane", createPaneExample()));
        tabPane.getTabs().add(tab("HBox", createHBoxExample()));
        tabPane.getTabs().add(tab("VBox", createVBoxExample()));
        tabPane.getTabs().add(tab("StackPane", createStackPaneExample()));
        tabPane.getTabs().add(tab("BorderPane", createBorderPaneExample()));
        tabPane.getTabs().add(tab("GridPane", createGridPaneExample()));
        tabPane.getTabs().add(tab("FlowPane", createFlowPaneExample()));
        tabPane.getTabs().add(tab("TilePane", createTilePaneExample()));
        tabPane.getTabs().add(tab("AnchorPane", createAnchorPaneExample()));
        tabPane.getTabs().add(tab("ScrollPane", createScrollPaneExample()));

        Scene scene = new Scene(tabPane, 600, 500);
        primaryStage.setTitle("JavaFX Layout Example");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Tab tab(String title, Pane content) {
        Tab tab = new Tab(title);
        tab.setContent(content);
        return tab;
    }

    private Pane createPaneExample() {
        Pane pane = new Pane();
        Rectangle rect = new Rectangle(50, 50, 100, 100);
        rect.setFill(Color.CORNFLOWERBLUE);
        pane.getChildren().add(rect);
        return pane;
    }

    private Pane createHBoxExample() {
        HBox hbox = new HBox(10, new Button("One"), new Button("Two"), new Button("Three"));
        hbox.setPadding(new Insets(10));
        hbox.setAlignment(Pos.CENTER);
        return hbox;
    }

    private Pane createVBoxExample() {
        VBox vbox = new VBox(10, new Label("Top"), new Button("Middle"), new TextField("Bottom"));
        vbox.setPadding(new Insets(10));
        vbox.setAlignment(Pos.CENTER);
        return vbox;
    }

    private Pane createStackPaneExample() {
        StackPane stackPane = new StackPane();
        Rectangle background = new Rectangle(200, 100, Color.LIGHTGRAY);
        Label label = new Label("On Top");
        stackPane.getChildren().addAll(background, label);
        stackPane.setPadding(new Insets(10));
        return stackPane;
    }

    private Pane createBorderPaneExample() {
        BorderPane borderPane = new BorderPane();
        borderPane.setTop(new Label("Top"));
        borderPane.setBottom(new Label("Bottom"));
        borderPane.setLeft(new Button("Left"));
        borderPane.setRight(new Button("Right"));
        borderPane.setCenter(new TextArea("Center"));
        borderPane.setPadding(new Insets(10));
        return borderPane;
    }

    private Pane createGridPaneExample() {
        GridPane grid = new GridPane();
        grid.setPadding(new Insets(10));
        grid.setHgap(10);
        grid.setVgap(10);
        grid.add(new Label("Name:"), 0, 0);
        grid.add(new TextField(), 1, 0);
        grid.add(new Label("Email:"), 0, 1);
        grid.add(new TextField(), 1, 1);
        return grid;
    }

    private Pane createFlowPaneExample() {
        FlowPane flow = new FlowPane(10, 10);
        flow.setPadding(new Insets(10));
        for (int i = 1; i <= 8; i++) {
            flow.getChildren().add(new Button("Button " + i));
        }
        return flow;
    }

    private Pane createTilePaneExample() {
        TilePane tile = new TilePane(10, 10);
        tile.setPadding(new Insets(10));
        tile.setPrefColumns(4);
        for (int i = 1; i <= 8; i++) {
            tile.getChildren().add(new Label("Tile " + i));
        }
        return tile;
    }

    private Pane createAnchorPaneExample() {
        AnchorPane anchor = new AnchorPane();
        Button button = new Button("Anchored Button");
        anchor.getChildren().add(button);
        AnchorPane.setTopAnchor(button, 20.0);
        AnchorPane.setLeftAnchor(button, 30.0);
        return anchor;
    }

    private Pane createScrollPaneExample() {
        VBox longVBox = new VBox(10);
        for (int i = 1; i <= 50; i++) {
            longVBox.getChildren().add(new Label("Line " + i));
        }
        ScrollPane scrollPane = new ScrollPane(longVBox);
        scrollPane.setFitToWidth(true);
        return new VBox(scrollPane);
    }

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

References

JavaFX GridPane Layout

2025/11/24

JavaFX Application

JavaFX 製作的 GUI application,因為以 java 實作,能夠跨平台運作。

application structure

Application (程式入口)
└── Stage (主視窗)
    └── Scene (場景)
        └── Scene Graph (節點樹)
            ├── Layouts (VBox, HBox, BorderPane, etc.)
            │   └── Controls (Button, Label, etc.)
            └── Other nodes (Canvas, WebView, etc.)
Scene (有一個 Root Node)
└── Root Node (Parent)
    ├── Branch Node (Parent)
    │   ├── Leaf Node (Control)
    │   └── Leaf Node (Shape)
    └── Branch Node (Group)
        └── Leaf Node (ImageView)

Stage

包含application內所有 objects,類別是 javafx.stage.Stage,primary stage 由 platform 建立,並以參數傳遞給 Application 的 start()

stage 有兩個參數決定位置:Width, Height,並分為 content area 與 decorations,呼叫show() 可顯示 stage 的 contents

Stage 是最上層的 container (window),只有一個主要的 Stage,javafx 支援五種類型的 stages

  • Primary Stage

    javafx runtime 產生的第一個初始 windows

    Main application window,由 star(Stage) 取得

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Primary Stage");
        primaryStage.show();
    }
  • Secondary Stage

    可用來產生dialogs, tools, secondary views

    其他 window/dialog,以 new Stage() 產生

    Stage secondaryStage = new Stage();
    secondaryStage.setTitle("Secondary Stage");
    secondaryStage.show();
  • Model Stage

    特殊的 secondary stage,會阻斷跟其他 window 的互動

    Blocking dialogs,new Stage() + initModality(..) 產生

    Stage modalStage = new Stage();
    modalStage.initModality(Modality.APPLICATION_MODAL);
    modalStage.setTitle("Modal Dialog");
    modalStage.showAndWait();  // blocks until closed
  • Transparent Stage

    沒有 title bar, border, shadow,沒有 decorations 的 stage

    Custom UI, shaped windows,以 initStyle(StageStyle.TRANSPARENT) 產生

    Stage transparentStage = new Stage();
    transparentStage.initStyle(StageStyle.TRANSPARENT);
    transparentStage.setScene(new Scene(root, 300, 200));
    transparentStage.getScene().setFill(Color.TRANSPARENT);
    transparentStage.show();
  • Undecorated/Utility

    Undecorated Stage: no title bar, no close/minimize/maximize buttons

    Utility Stage: styled like a small tool window (depends on OS), useful for small popups or tool palettes.

    Tool windows, splash screens,以 StageStyle.UNDECORATED/UTILITY 產生

    Stage undecoratedStage = new Stage();
    undecoratedStage.initStyle(StageStyle.UNDECORATED);
    undecoratedStage.show();
    
    Stage utilityStage = new Stage();
    utilityStage.initStyle(StageStyle.UTILITY);
    utilityStage.show();

Scene

scene 包含了 scene graph 所有內容,類別是 javafx.scene.Scene,每一個 scene object instance 只能加入一個唯一的 stage

Scene Graph and Nodes

scene graph 是樹狀結構,裡面是 scene 的內容, node 是 scene graph 裡面的 visual/graphical object

  • Container (Parent)

    可包含子節點,用來排版,是 branch node 不是 leaf

    類別 說明
    Group 不處理排版,純粹容器
    Region 所有 layout class 基礎
    Pane 絕對位置排版
    VBox, HBox 垂直 / 水平排版容器
    BorderPane 上下左右中五區域排版
    StackPane 子節點重疊排列
    GridPane 表格式佈局
    AnchorPane 固定邊界排版(類似 CSS)
  • Control (互動元件)

    是 leaf node,可直接跟 user 互動

    類別 說明
    Button 按鈕
    Label 顯示文字
    TextField 單行文字輸入
    TextArea 多行文字輸入
    CheckBox 核取框
    RadioButton 單選按鈕
    ComboBox 下拉選單
    ListView 清單顯示
    TableView 表格資料顯示
    TreeView 樹狀階層顯示
    Slider 拖拉式數值選擇器
    ProgressBar 進度條
    MenuBar 選單列
    TabPane 分頁容器
  • Geometrical (graphical) 2D/3D object (Shape)

    leaf node,用在自訂 UI,或是繪圖功能

    類別 說明
    Rectangle 矩形
    Circle 圓形
    Ellipse 橢圓
    Line 線條
    Polygon 多邊形
    Polyline 折線
    Arc 弧形
    Path 任意路徑
  • Media

    Audio/Video/Image

    類別 說明
    ImageView 顯示圖片
    MediaView 播放音訊或影片視訊
    Canvas 提供 2D 自訂繪圖區域
    WebView 嵌入網頁(HTML/CSS/JS)
    Text 顯示純文字(非 Control)
  • Transform/特效用 node

    類別 說明
    SubScene 場景中的獨立子場景
    SnapshotView 拍攝其他節點的快照
    Group 提供無排版的節點群組
    Clip 裁剪圖形
    Effect (陰影等) 雖不是 Node,但可附加在 Node 上

Node 有三類

  • Root Node

  • Branch/Parent Node

    • Group

    • Region

    • WebView

  • Leaf Node

Example

import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class JavafxSample extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        //Creating a line object
        Line line = new Line();

        //Setting the properties to a line
        line.setStartX(100.0);
        line.setStartY(150.0);
        line.setEndX(500.0);
        line.setEndY(150.0);

        //Creating a Text object
        Text text = new Text();
        //Setting font to the text
        text.setFont(new Font(45));
        //setting the position of the text
        text.setX(50);
        text.setY(150);
        //Setting the text to be added.
        text.setText("Welcome to JavaFx Demo");

        //creating a Group object
        Group group = new Group( );
        //Retrieving the observable list object
        ObservableList list = group.getChildren();
        //Setting the text object as a node to the group object
        list.add(text);
        list.add(line);


        //Creating a Scene by passing the group object, height and width   
        Scene scene = new Scene(group, 600, 300);
        //setting color to the scene 
        scene.setFill(Color.ALICEBLUE);

        //Setting the title to Stage. 
        primaryStage.setTitle("Sample Application");
        //Adding the scene to Stage 
        primaryStage.setScene(scene);
        //Displaying the contents of the stage 
        primaryStage.show();
    }

    public static void main(String args[]) {
        launch(args);
    }
}
java --module-path /java/javafx-sdk-25.0.1/lib/ --add-modules javafx.controls JavafxSample

執行結果