2025/12/29

JavaFX 06 tranformation, transition

javafx 可套用 transformation 在單一或一組 nodes,也可以針對某一個 node,同時套用多個 transformations。

transformation

transformation 有以下這幾種

  • Rotation:旋轉
  • Scaling:縮放
  • Translation:平移
  • Shearing(斜切):用仿射變換實現
package javafx.transformation;

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

//Rotation:旋轉
//Scaling:縮放
//Translation:平移
//Shearing(斜切):用仿射變換實現
public class TransformDemo extends Application {

    @Override
    public void start(Stage primaryStage) {
        // === 建立形狀 ===
        Rectangle rect = new Rectangle(100, 60, Color.CORNFLOWERBLUE);
        rect.setArcWidth(15);
        rect.setArcHeight(15);

        // === 各種變換按鈕 ===
        Button btnTranslate = new Button("Translate (+100, +50)");
        btnTranslate.setOnAction(e -> {
            rect.getTransforms().add(new Translate(100, 50));
        });

        Button btnRotate = new Button("Rotate 45°");
        btnRotate.setOnAction(e -> {
            rect.getTransforms().add(new Rotate(45, rect.getX() + rect.getWidth() / 2, rect.getY() + rect.getHeight() / 2));
        });

        Button btnScale = new Button("Scale x1.5");
        btnScale.setOnAction(e -> {
            rect.getTransforms().add(new Scale(1.5, 1.5, rect.getX() + rect.getWidth() / 2, rect.getY() + rect.getHeight() / 2));
        });

        Button btnShear = new Button("Shear x=0.5, y=0");
        btnShear.setOnAction(e -> {
            rect.getTransforms().add(new Shear(0.5, 0));
        });

        // === Reset 按鈕 ===
        Button btnReset = new Button("Reset Transforms");
        btnReset.setStyle("-fx-background-color: tomato; -fx-text-fill: white;");
        btnReset.setOnAction(e -> {
            rect.getTransforms().clear(); // 移除所有變換
        });

        // === 佈局 ===
        VBox root = new VBox(15,
                rect,
                btnTranslate,
                btnRotate,
                btnScale,
                btnShear,
                btnReset
        );
        root.setPadding(new Insets(20));

        Scene scene = new Scene(root, 500, 400);
        primaryStage.setTitle("JavaFX Transformations with Reset");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

transition

transform 是一種即時的直接的轉換,會直接改變 node 的屬性,立刻生效。用作 node 的幾何變換。

transition 是一種動畫,需要一段時間,才會轉換到結果的動畫,可以 delay, repeat,有移動、縮放、旋轉、淡入淡出、路徑移動、視覺強化這些動態轉換的效果。

package javafx.transition;

import javafx.animation.*;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;

public class AllTransitionsDemo extends Application {

    private Rectangle rect;

    // 紀錄狀態
    private double currentTranslateX = 0;
    private double currentTranslateY = 0;
    private double currentRotate = 0;
    private double currentScaleX = 1;
    private double currentScaleY = 1;

    @Override
    public void start(Stage primaryStage) {
        rect = new Rectangle(100, 60, Color.CORNFLOWERBLUE);
        rect.setArcWidth(15);
        rect.setArcHeight(15);
        rect.setStroke(Color.DARKBLUE);
        rect.setStrokeWidth(3);

        // RotateTransition
        Button btnRotate = new Button("Rotate +45°");
        btnRotate.setOnAction(e -> {
            currentRotate += 45;
            RotateTransition rt = new RotateTransition(Duration.seconds(1), rect);
            rt.setToAngle(currentRotate);
            rt.play();
        });

        // ScaleTransition
        Button btnScale = new Button("Scale x1.5");
        btnScale.setOnAction(e -> {
            currentScaleX *= 1.5;
            currentScaleY *= 1.5;
            ScaleTransition st = new ScaleTransition(Duration.seconds(1), rect);
            st.setToX(currentScaleX);
            st.setToY(currentScaleY);
            st.play();
        });

        // TranslateTransition
        Button btnTranslate = new Button("Translate (+100, +50)");
        btnTranslate.setOnAction(e -> {
            currentTranslateX += 100;
            currentTranslateY += 50;
            TranslateTransition tt = new TranslateTransition(Duration.seconds(1), rect);
            tt.setToX(currentTranslateX);
            tt.setToY(currentTranslateY);
            tt.play();
        });

        // FadeTransition
        Button btnFade = new Button("Fade Out / In");
        btnFade.setOnAction(e -> {
            FadeTransition ft = new FadeTransition(Duration.seconds(1), rect);
            ft.setFromValue(1.0);
            ft.setToValue(0.2);
            ft.setAutoReverse(true);
            ft.setCycleCount(2);
            ft.play();
        });

        // FillTransition (填充色)
        Button btnFill = new Button("Fill Color Change");
        btnFill.setOnAction(e -> {
            FillTransition fillTransition = new FillTransition(Duration.seconds(2), rect);
            fillTransition.setFromValue((Color) rect.getFill());
            fillTransition.setToValue(Color.ORANGERED);
            fillTransition.setAutoReverse(true);
            fillTransition.setCycleCount(2);
            fillTransition.play();
        });

        // StrokeTransition (邊框色)
        Button btnStroke = new Button("Stroke Color Change");
        btnStroke.setOnAction(e -> {
            StrokeTransition strokeTransition = new StrokeTransition(Duration.seconds(2), rect);
            strokeTransition.setFromValue((Color) rect.getStroke());
            strokeTransition.setToValue(Color.GREEN);
            strokeTransition.setAutoReverse(true);
            strokeTransition.setCycleCount(2);
            strokeTransition.play();
        });

        // SequentialTransition
        Button btnSequential = new Button("Sequential Animation");
        btnSequential.setOnAction(e -> {
            RotateTransition rotate = new RotateTransition(Duration.seconds(1), rect);
            rotate.setByAngle(90);

            TranslateTransition translate = new TranslateTransition(Duration.seconds(1), rect);
            translate.setByX(100);
            translate.setByY(50);

            ScaleTransition scale = new ScaleTransition(Duration.seconds(1), rect);
            scale.setToX(1);
            scale.setToY(1);

            SequentialTransition seq = new SequentialTransition(rect, rotate, translate, scale);
            seq.play();
        });

        // ParallelTransition
        Button btnParallel = new Button("Parallel Animation");
        btnParallel.setOnAction(e -> {
            RotateTransition rotate = new RotateTransition(Duration.seconds(2), rect);
            rotate.setByAngle(180);

            FadeTransition fade = new FadeTransition(Duration.seconds(2), rect);
            fade.setFromValue(1);
            fade.setToValue(0.3);

            ParallelTransition parallel = new ParallelTransition(rect, rotate, fade);
            parallel.play();
        });

        // PauseTransition
        Button btnPause = new Button("Pause 2 Seconds");
        btnPause.setOnAction(e -> {
            PauseTransition pause = new PauseTransition(Duration.seconds(2));
            pause.setOnFinished(ev -> System.out.println("Pause finished"));
            pause.play();
        });

        // PathTransition
        Button btnPath = new Button("Path Animation");
        btnPath.setOnAction(e -> {
            Path path = new Path();
            path.getElements().add(new MoveTo(0, 0));
            path.getElements().add(new CubicCurveTo(150, 100, 300, 0, 400, 100));

            PathTransition pathTransition = new PathTransition(Duration.seconds(3), path, rect);
            pathTransition.setCycleCount(1);
            pathTransition.play();
        });

        // Reset 按鈕
        Button btnReset = new Button("Reset All");
        btnReset.setStyle("-fx-background-color: tomato; -fx-text-fill: white;");
        btnReset.setOnAction(e -> {
            rect.getTransforms().clear();
            rect.setTranslateX(0);
            rect.setTranslateY(0);
            rect.setRotate(0);
            rect.setScaleX(1);
            rect.setScaleY(1);
            rect.setOpacity(1);
            rect.setFill(Color.CORNFLOWERBLUE);
            rect.setStroke(Color.DARKBLUE);

            currentTranslateX = 0;
            currentTranslateY = 0;
            currentRotate = 0;
            currentScaleX = 1;
            currentScaleY = 1;
        });

        VBox root = new VBox(10,
                rect,
                btnRotate,
                btnScale,
                btnTranslate,
                btnFade,
                btnFill,
                btnStroke,
                btnSequential,
                btnParallel,
                btnPause,
                btnPath,
                btnReset
        );
        root.setPadding(new Insets(15));

        Scene scene = new Scene(root, 600, 700);
        primaryStage.setTitle("JavaFX All Transitions Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

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);
    }
}