2026/01/12

JavaFX 08 webview

WebView 是一個可以嵌入網頁瀏覽功能的元件,使用內建的 WebKit 引擎來顯示 HTML 內容(包括 JavaScript、CSS 等)。這對於桌面應用程式需要嵌入網頁內容(如:顯示文件、使用 Web UI 元件)時非常有用。

但該 WebKit engine 不是最新版本,雖然支援 html5/css/javascript,但不支援新的標準,不支援多個分頁,沒有 js console 開發者工具,無法使用 react/vue 這些框架,不能跟 javafx node 直接互動(無法將 node 插入 html DOM)。

webview 裡面的頁面跟 js,可以跟 java code 互動

以下是一個雙向互動的 sample

js 可可

package javafx.webview;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;

public class WebViewDemo extends Application {

    @Override
    public void start(Stage primaryStage) {
        WebView webView = new WebView();
        WebEngine webEngine = webView.getEngine();

        // 載入 HTML 字串
        String html = """
            <html>
            <head>
                <script>
                    function callJava() {
                        javaApp.showMessage("Hello from JavaScript!");
                    }

                    function fromJava(data) {
                        document.getElementById("result").innerText = "Java says: " + data;
                    }
                </script>
            </head>
            <body>
                <h2>JavaFX WebView 雙向互動</h2>
                <button onclick="callJava()">呼叫 Java 方法</button>
                <p id="result"></p>
            </body>
            </html>
            """;

        webEngine.loadContent(html);

        // 頁面載入完成後建立 bridge
        webEngine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
            if (newState == javafx.concurrent.Worker.State.SUCCEEDED) {
                JSObject window = (JSObject) webEngine.executeScript("window");
                window.setMember("javaApp", new JavaBridge(webEngine));
            }
        });

        BorderPane root = new BorderPane(webView);
        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.setTitle("JavaFX WebView 雙向互動範例");
        primaryStage.show();
    }

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

    // Java Bridge 類別
    public static class JavaBridge {
        private final WebEngine webEngine;

        public JavaBridge(WebEngine webEngine) {
            this.webEngine = webEngine;
        }

        // JS 呼叫這個方法
        public void showMessage(String msg) {
            System.out.println("JavaScript 傳來訊息: " + msg);
            // Java 反呼叫 JS 的 function
            webEngine.executeScript("fromJava('收到: " + msg + "')");
        }
    }
}

2026/01/05

JavaFX 07 chart

用 javafx 繪製圖表的工具

  • 圓餅圖 PieChart 無需軸
  • 折線圖 LineChart<X,Y> 通常 CategoryAxis/NumberAxis
  • 面積圖 AreaChart<X,Y> 同折線圖
  • 長條圖 BarChart<X,Y> 通常 CategoryAxis/NumberAxis
  • 氣泡圖 BubbleChart<Number,Number> 用第三值表示半徑
  • 散點圖 ScatterChart<Number,Number> 點陣圖
  • 累積面積圖 StackedAreaChart<X,Y> 多組堆疊數據區域
  • 累積長條圖 StackedBarChart<X,Y> 長條堆疊比較
package javafx.chart;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.chart.*;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

//圖表類型    JavaFX 類別名稱    X/Y 軸類型
//圓餅圖    PieChart    無需軸
//折線圖    LineChart<X,Y>    通常 CategoryAxis/NumberAxis
//面積圖    AreaChart<X,Y>    同折線圖
//長條圖    BarChart<X,Y>    通常 CategoryAxis/NumberAxis
//氣泡圖    BubbleChart<Number,Number>    用第三值表示半徑
//散點圖    ScatterChart<Number,Number>    點陣圖
//累積面積圖    StackedAreaChart<X,Y>    多組堆疊數據區域
//累積長條圖    StackedBarChart<X,Y>    長條堆疊比較

public class AllChartsDemo extends Application {

    private BorderPane root;
    private ComboBox<String> chartSelector;

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

        chartSelector = new ComboBox<>();
        chartSelector.getItems().addAll(
                "PieChart", "LineChart", "AreaChart", "BarChart",
                "BubbleChart", "ScatterChart", "StackedAreaChart", "StackedBarChart"
        );
        chartSelector.setValue("PieChart");
        chartSelector.setOnAction(e -> updateChart(chartSelector.getValue()));

        root.setTop(chartSelector);
        updateChart(chartSelector.getValue());

        Scene scene = new Scene(root, 800, 600);
        primaryStage.setTitle("JavaFX Chart Selector Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void updateChart(String type) {
        switch (type) {
            case "PieChart":
                PieChart pieChart = new PieChart();
                pieChart.setTitle("Pie Chart");
                pieChart.getData().addAll(
                        new PieChart.Data("Java", 30),
                        new PieChart.Data("Python", 25),
                        new PieChart.Data("C++", 20),
                        new PieChart.Data("Kotlin", 15),
                        new PieChart.Data("Other", 10)
                );
                root.setCenter(pieChart);
                break;

            case "LineChart":
                CategoryAxis x1 = new CategoryAxis();
                NumberAxis y1 = new NumberAxis();
                LineChart<String, Number> lineChart = new LineChart<>(x1, y1);
                lineChart.setTitle("Line Chart");
                XYChart.Series<String, Number> lineSeries = new XYChart.Series<>();
                lineSeries.setName("Sales");
                lineSeries.getData().add(new XYChart.Data<>("Mon", 10));
                lineSeries.getData().add(new XYChart.Data<>("Tue", 20));
                lineSeries.getData().add(new XYChart.Data<>("Wed", 15));
                lineSeries.getData().add(new XYChart.Data<>("Thu", 25));
                lineSeries.getData().add(new XYChart.Data<>("Fri", 18));
                lineChart.getData().add(lineSeries);
                root.setCenter(lineChart);
                break;

            case "AreaChart":
                AreaChart<String, Number> areaChart = new AreaChart<>(new CategoryAxis(), new NumberAxis());
                areaChart.setTitle("Area Chart");
                XYChart.Series<String, Number> areaSeries = new XYChart.Series<>();
                areaSeries.setName("Traffic");
                areaSeries.getData().add(new XYChart.Data<>("Jan", 200));
                areaSeries.getData().add(new XYChart.Data<>("Feb", 300));
                areaSeries.getData().add(new XYChart.Data<>("Mar", 150));
                areaSeries.getData().add(new XYChart.Data<>("Apr", 250));
                areaChart.getData().add(areaSeries);
                root.setCenter(areaChart);
                break;

            case "BarChart":
                BarChart<String, Number> barChart = new BarChart<>(new CategoryAxis(), new NumberAxis());
                barChart.setTitle("Bar Chart");
                XYChart.Series<String, Number> barSeries = new XYChart.Series<>();
                barSeries.setName("Downloads");
                barSeries.getData().add(new XYChart.Data<>("Chrome", 60));
                barSeries.getData().add(new XYChart.Data<>("Firefox", 40));
                barSeries.getData().add(new XYChart.Data<>("Edge", 30));
                barChart.getData().add(barSeries);
                root.setCenter(barChart);
                break;

            case "BubbleChart":
                BubbleChart<Number, Number> bubbleChart = new BubbleChart<>(new NumberAxis(), new NumberAxis());
                bubbleChart.setTitle("Bubble Chart");
                XYChart.Series<Number, Number> bubbleSeries = new XYChart.Series<>();
                bubbleSeries.setName("Bubbles");
                bubbleSeries.getData().add(new XYChart.Data<>(10, 20, 5));
                bubbleSeries.getData().add(new XYChart.Data<>(15, 30, 10));
                bubbleSeries.getData().add(new XYChart.Data<>(25, 10, 7));
                bubbleChart.getData().add(bubbleSeries);
                root.setCenter(bubbleChart);
                break;

            case "ScatterChart":
                ScatterChart<Number, Number> scatterChart = new ScatterChart<>(new NumberAxis(), new NumberAxis());
                scatterChart.setTitle("Scatter Chart");
                XYChart.Series<Number, Number> scatterSeries = new XYChart.Series<>();
                scatterSeries.setName("Points");
                scatterSeries.getData().add(new XYChart.Data<>(5, 20));
                scatterSeries.getData().add(new XYChart.Data<>(10, 40));
                scatterSeries.getData().add(new XYChart.Data<>(15, 25));
                scatterChart.getData().add(scatterSeries);
                root.setCenter(scatterChart);
                break;

            case "StackedAreaChart":
                StackedAreaChart<String, Number> stackedAreaChart = new StackedAreaChart<>(new CategoryAxis(), new NumberAxis());
                stackedAreaChart.setTitle("Stacked Area Chart");
                XYChart.Series<String, Number> sa1 = new XYChart.Series<>();
                sa1.setName("Team A");
                sa1.getData().add(new XYChart.Data<>("Q1", 100));
                sa1.getData().add(new XYChart.Data<>("Q2", 120));
                sa1.getData().add(new XYChart.Data<>("Q3", 90));
                sa1.getData().add(new XYChart.Data<>("Q4", 110));
                XYChart.Series<String, Number> sa2 = new XYChart.Series<>();
                sa2.setName("Team B");
                sa2.getData().add(new XYChart.Data<>("Q1", 80));
                sa2.getData().add(new XYChart.Data<>("Q2", 95));
                sa2.getData().add(new XYChart.Data<>("Q3", 70));
                sa2.getData().add(new XYChart.Data<>("Q4", 85));
                stackedAreaChart.getData().addAll(sa1, sa2);
                root.setCenter(stackedAreaChart);
                break;

            case "StackedBarChart":
                StackedBarChart<String, Number> stackedBarChart = new StackedBarChart<>(new CategoryAxis(), new NumberAxis());
                stackedBarChart.setTitle("Stacked Bar Chart");
                XYChart.Series<String, Number> sb1 = new XYChart.Series<>();
                sb1.setName("Male");
                sb1.getData().add(new XYChart.Data<>("2021", 60));
                sb1.getData().add(new XYChart.Data<>("2022", 70));
                XYChart.Series<String, Number> sb2 = new XYChart.Series<>();
                sb2.setName("Female");
                sb2.getData().add(new XYChart.Data<>("2021", 40));
                sb2.getData().add(new XYChart.Data<>("2022", 55));
                stackedBarChart.getData().addAll(sb1, sb2);
                root.setCenter(stackedBarChart);
                break;
        }
    }

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