2025/12/15

JavaFX 05 2d

javafx 可直接在 pane 上面畫上 2d/3d shape。

Shape

預設支援的 shape 有以下這些

  • Line:直線

  • Rectangle:矩形

  • Rounded Rectangle:圓角矩形 (setArcWidth / setArcHeight)

  • Circle:圓形

  • Ellipse:橢圓

  • Polygon:多邊形(此處為三角形)

  • CubicCurve:三次貝茲曲線

  • QuadCurve:二次貝茲曲線

  • Arc:弧線,可設定弧度長度與型態

  • SVGPath:以 SVG 路徑字串定義任意形狀

以下是所有 shape 的 demo

package javafx.shape2d;

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

//Line:直線
//Rectangle:矩形
//Rounded Rectangle:圓角矩形 (setArcWidth / setArcHeight)
//Circle:圓形
//Ellipse:橢圓
//Polygon:多邊形(此處為三角形)
//CubicCurve:三次貝茲曲線
//QuadCurve:二次貝茲曲線
//Arc:弧線,可設定弧度長度與型態
//SVGPath:以 SVG 路徑字串定義任意形狀(此例為星形)
public class ShapesDemo extends Application {

    @Override
    public void start(Stage primaryStage) {
        VBox root = new VBox(15);
        root.setPadding(new Insets(20));

        // Line
        Line line = new Line(0, 0, 150, 0);
        line.setStrokeWidth(3);
        line.setStroke(Color.BLUE);

        // Rectangle
        Rectangle rectangle = new Rectangle(150, 80);
        rectangle.setFill(Color.LIGHTGREEN);
        rectangle.setStroke(Color.DARKGREEN);
        rectangle.setStrokeWidth(2);

        // Rounded Rectangle
        Rectangle roundedRect = new Rectangle(150, 80);
        roundedRect.setArcWidth(30);
        roundedRect.setArcHeight(30);
        roundedRect.setFill(Color.LIGHTCORAL);
        roundedRect.setStroke(Color.DARKRED);
        roundedRect.setStrokeWidth(2);

        // Circle
        Circle circle = new Circle(40);
        circle.setFill(Color.LIGHTBLUE);
        circle.setStroke(Color.DARKBLUE);
        circle.setStrokeWidth(2);

        // Ellipse
        Ellipse ellipse = new Ellipse(60, 40);
        ellipse.setFill(Color.LIGHTYELLOW);
        ellipse.setStroke(Color.GOLDENROD);
        ellipse.setStrokeWidth(2);

        // Polygon (triangle)
        Polygon polygon = new Polygon();
        polygon.getPoints().addAll(
                0.0, 0.0,
                100.0, 0.0,
                50.0, 80.0
        );
        polygon.setFill(Color.PLUM);
        polygon.setStroke(Color.PURPLE);
        polygon.setStrokeWidth(2);

        // Cubic Curve
        CubicCurve cubicCurve = new CubicCurve();
        cubicCurve.setStartX(10);
        cubicCurve.setStartY(50);
        cubicCurve.setControlX1(80);
        cubicCurve.setControlY1(10);
        cubicCurve.setControlX2(130);
        cubicCurve.setControlY2(90);
        cubicCurve.setEndX(200);
        cubicCurve.setEndY(50);
        cubicCurve.setStroke(Color.TEAL);
        cubicCurve.setStrokeWidth(3);
        cubicCurve.setFill(Color.TRANSPARENT);

        // Quad Curve
        QuadCurve quadCurve = new QuadCurve();
        quadCurve.setStartX(10);
        quadCurve.setStartY(100);
        quadCurve.setControlX(100);
        quadCurve.setControlY(50);
        quadCurve.setEndX(200);
        quadCurve.setEndY(100);
        quadCurve.setStroke(Color.ORANGE);
        quadCurve.setStrokeWidth(3);
        quadCurve.setFill(Color.TRANSPARENT);

        // Arc
        Arc arc = new Arc();
        arc.setCenterX(100);
        arc.setCenterY(100);
        arc.setRadiusX(80);
        arc.setRadiusY(50);
        arc.setStartAngle(45);
        arc.setLength(270);
        arc.setType(ArcType.ROUND);
        arc.setFill(Color.LIGHTPINK);
        arc.setStroke(Color.HOTPINK);
        arc.setStrokeWidth(2);

        // SVGPath (star shape)
        SVGPath svgPath = new SVGPath();
        svgPath.setContent("M100,10 L40,198 L190,78 L10,78 L160,198 Z");
        svgPath.setFill(Color.GOLD);
        svgPath.setStroke(Color.ORANGE);
        svgPath.setStrokeWidth(2);

        root.getChildren().addAll(
                line,
                rectangle,
                roundedRect,
                circle,
                ellipse,
                polygon,
                cubicCurve,
                quadCurve,
                arc,
                svgPath
        );

        Scene scene = new Scene(root, 400, 900);
        primaryStage.setTitle("JavaFX 2D Shapes Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Properties

2d shape 的 properties 有以下這些 method

屬性 用途說明
StrokeType 邊框繪製位置,三種:CENTERED(預設)、INSIDEOUTSIDE
StrokeWidth 邊框寬度
Stroke 邊框顏色
StrokeLineJoin 連接線段時的連接樣式:MITERBEVELROUND
StrokeMiterLimit MITER 連接時,限制尖角長度
StrokeLineCap 線段端點樣式:BUTTROUNDSQUARE
Smooth 是否啟用平滑繪製(抗鋸齒效果)

sample code:

package javafx.shape2d;

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

public class ShapeStrokePropertiesDemo extends Application {

    @Override
    public void start(Stage primaryStage) {
        VBox root = new VBox(20);
        root.setPadding(new Insets(20));

        // Rectangle 1: StrokeType.CENTERED (預設)
        Rectangle rect1 = new Rectangle(150, 60);
        rect1.setFill(Color.LIGHTBLUE);
        rect1.setStroke(Color.DARKBLUE);
        rect1.setStrokeWidth(8);
        rect1.setStrokeType(StrokeType.CENTERED);
        rect1.setSmooth(true);

        // Rectangle 2: StrokeType.INSIDE
        Rectangle rect2 = new Rectangle(150, 60);
        rect2.setFill(Color.LIGHTGREEN);
        rect2.setStroke(Color.DARKGREEN);
        rect2.setStrokeWidth(8);
        rect2.setStrokeType(StrokeType.INSIDE);
        rect2.setSmooth(false);

        // Rectangle 3: StrokeType.OUTSIDE
        Rectangle rect3 = new Rectangle(150, 60);
        rect3.setFill(Color.PEACHPUFF);
        rect3.setStroke(Color.ORANGE);
        rect3.setStrokeWidth(8);
        rect3.setStrokeType(StrokeType.OUTSIDE);
        rect3.setSmooth(true);

        // Polygon with different StrokeLineJoin
        Polygon polyMiter = new Polygon(0,0, 100,0, 50,80);
        polyMiter.setFill(Color.TRANSPARENT);
        polyMiter.setStroke(Color.CRIMSON);
        polyMiter.setStrokeWidth(12);
        polyMiter.setStrokeLineJoin(StrokeLineJoin.MITER);
        polyMiter.setStrokeMiterLimit(10);
        polyMiter.setSmooth(true);

        Polygon polyBevel = new Polygon(0,0, 100,0, 50,80);
        polyBevel.setFill(Color.TRANSPARENT);
        polyBevel.setStroke(Color.DARKMAGENTA);
        polyBevel.setStrokeWidth(12);
        polyBevel.setStrokeLineJoin(StrokeLineJoin.BEVEL);
        polyBevel.setSmooth(true);

        Polygon polyRound = new Polygon(0,0, 100,0, 50,80);
        polyRound.setFill(Color.TRANSPARENT);
        polyRound.setStroke(Color.DARKORANGE);
        polyRound.setStrokeWidth(12);
        polyRound.setStrokeLineJoin(StrokeLineJoin.ROUND);
        polyRound.setSmooth(true);

        // Line with different StrokeLineCap
        Line lineButt = new Line(0, 0, 150, 0);
        lineButt.setStroke(Color.DARKCYAN);
        lineButt.setStrokeWidth(12);
        lineButt.setStrokeLineCap(StrokeLineCap.BUTT);
        lineButt.setSmooth(true);

        Line lineRound = new Line(0, 0, 150, 0);
        lineRound.setStroke(Color.TEAL);
        lineRound.setStrokeWidth(12);
        lineRound.setStrokeLineCap(StrokeLineCap.ROUND);
        lineRound.setSmooth(true);

        Line lineSquare = new Line(0, 0, 150, 0);
        lineSquare.setStroke(Color.MEDIUMVIOLETRED);
        lineSquare.setStrokeWidth(12);
        lineSquare.setStrokeLineCap(StrokeLineCap.SQUARE);
        lineSquare.setSmooth(true);

        VBox shapesGroup = new VBox(10,
                rect1,
                rect2,
                rect3,
                polyMiter,
                polyBevel,
                polyRound,
                lineButt,
                lineRound,
                lineSquare
        );

        root.getChildren().add(shapesGroup);

        Scene scene = new Scene(root, 400, 800);
        primaryStage.setTitle("JavaFX Shape Stroke Properties Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Operations

2d shape 的operations 有三種

  • union 聯集

  • intersect 交集

  • substract 差集

sample code

package javafx.shape2d;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Text;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ShapeOperationsDemo extends Application {

    @Override
    public void start(Stage primaryStage) {
        // 原始兩個圓形
        Circle circle1 = new Circle(80, 80, 70, Color.LIGHTBLUE);
        Circle circle2 = new Circle(130, 80, 70, Color.LIGHTCORAL);

        circle1.setStroke(Color.BLUE);
        circle1.setStrokeWidth(2);
        circle2.setStroke(Color.RED);
        circle2.setStrokeWidth(2);

        // Union 聯集
        Shape union = Shape.union(circle1, circle2);
        union.setFill(Color.LIGHTGREEN);
        union.setStroke(Color.DARKGREEN);
        union.setStrokeWidth(2);

        // Intersection 交集
        Shape intersect = Shape.intersect(circle1, circle2);
        intersect.setFill(Color.ORANGE);
        intersect.setStroke(Color.DARKORANGE);
        intersect.setStrokeWidth(2);

        // Subtract 差集 circle1 - circle2
        Shape subtract = Shape.subtract(circle1, circle2);
        subtract.setFill(Color.PLUM);
        subtract.setStroke(Color.PURPLE);
        subtract.setStrokeWidth(2);

        // 佈局顯示,每個下方有說明文字
        VBox vUnion = new VBox(5, union, new Text("Union"));
        VBox vIntersect = new VBox(5, intersect, new Text("Intersection"));
        VBox vSubtract = new VBox(5, subtract, new Text("Subtract"));

        HBox root = new HBox(30, vUnion, vIntersect, vSubtract);
        root.setPadding(new Insets(20));

        Scene scene = new Scene(root, 700, 250);
        primaryStage.setTitle("JavaFX Shape Boolean Operations");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Path

Path 可用來繪製任意形狀複雜的圖案

  • MoveTo(x,y) 設定起點位置
  • LineTo(x,y) 從前一點畫直線到指定點
  • HLineTo(x) 從前一點畫水平線到 x 軸座標
  • VLineTo(y) 從前一點畫垂直線到 y 軸座標
  • QuadCurveTo 畫二次貝茲曲線(1 控制點)
  • CubicCurveTo 畫三次貝茲曲線(2 控制點)
  • ArcTo 畫圓弧(需設定半徑、終點、sweep 等)

sample code:

package javafx.shape2d;

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

//MoveTo(x,y)    設定起點位置
//LineTo(x,y)    從前一點畫直線到指定點
//HLineTo(x)    從前一點畫水平線到 x 軸座標
//VLineTo(y)    從前一點畫垂直線到 y 軸座標
//QuadCurveTo    畫二次貝茲曲線(1 控制點)
//CubicCurveTo    畫三次貝茲曲線(2 控制點)
//ArcTo    畫圓弧(需設定半徑、終點、sweep 等)
public class PathDemo extends Application {

    @Override
    public void start(Stage primaryStage) {
        Path path = new Path();
        path.setStroke(Color.DARKBLUE);
        path.setStrokeWidth(3);
        path.setFill(Color.LIGHTBLUE.deriveColor(1, 1, 1, 0.3)); // 半透明填色

        // 1. MoveTo 起點
        MoveTo moveTo = new MoveTo(50, 50);

        // 2. LineTo: 畫直線
        LineTo lineTo = new LineTo(150, 50);

        // 3. HLineTo: 畫水平線
        HLineTo hLineTo = new HLineTo(250);

        // 4. VLineTo: 畫垂直線
        VLineTo vLineTo = new VLineTo(150);

        // 5. QuadCurveTo: 二次貝茲曲線
        QuadCurveTo quadCurveTo = new QuadCurveTo(200, 200, 150, 250);

        // 6. CubicCurveTo: 三次貝茲曲線
        CubicCurveTo cubicCurveTo = new CubicCurveTo(100, 300, 50, 200, 50, 150);

        // 7. ArcTo: 畫弧線
        ArcTo arcTo = new ArcTo();
        arcTo.setX(50);
        arcTo.setY(50);  // 回到起點形成封閉形狀
        arcTo.setRadiusX(30);
        arcTo.setRadiusY(30);
        arcTo.setSweepFlag(true);

        path.getElements().addAll(
                moveTo,
                lineTo,
                hLineTo,
                vLineTo,
                quadCurveTo,
                cubicCurveTo,
                arcTo
        );

        StackPane root = new StackPane(path);
        root.setPadding(new Insets(20));

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

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

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