简介这份资源面向需要在Java桌面应用中嵌入网页并展示Echarts图表的开发者重点解决SWT与JavaFX两套GUI框架的整合问题。通过SWT的Browser组件与JavaFX的WebView协同工作可在原生窗口中加载现代Web页面并针对Echarts数据刷新效率与分片上传场景给出可运行示例适合具备一定Java桌面开发基础的中高级开发者参考。压缩包为rar格式共72个文件约129.56MB包含44个class、12个java源码、6个jar依赖库以及4个html页面等其中jar包涉及jxbrowser与jfxswt等浏览器桥接组件源码与class便于直接运行和二次调试。目前已有327人学习下载。资源完整呈现了SWT Shell承载JavaFX Stage、WebView加载Echarts、分片上传后触发图表刷新等关键实现读者可据此掌握跨框架整合思路与性能优化方向减少自行摸索成本。1. 当 SWT 遇上 JavaFX把 ECharts 塞进桌面端的真实折腾记录做过 Java 桌面端的人大概都遇到过这个需求主界面用 SWT 搭好了控件、布局、事件循环都跑得挺稳结果产品经理过来说“图表能不能做得像网页那样好看点”。你打开 ECharts 官网一看效果确实漂亮但那是 JavaScript 的东西怎么塞进 Java 桌面程序里常见的做法有两种一是用 SWT 自带的Browser控件直接加载一个本地 HTML 页面页面里跑 ECharts二是用 JavaFX 的WebView来承载网页内容再通过JFXPanel嵌回 SWT 容器。两条路都能走通但坑点完全不同。这份资源围绕的就是“SWT JavaFX 整合 自带浏览器 调用网页 ECharts”这条链路适合已经有一定 Java 桌面开发经验、需要在传统 SWT 项目里快速接入现代化图表的从业者。下面把我实际拆解和复现的过程完整写出来包括选型理由、代码步骤和几个让我翻车好几次的细节。2. 选型先想清楚SWT Browser 还是 JavaFX WebView2.1 两种浏览器控件的本质差异SWT 的Browser控件底层依赖操作系统的原生浏览器内核。Windows 上走的是 IE 或者 Edge WebView2Linux 上通常是 WebKitGTKmacOS 上则是 Safari 的 WebKit。这意味着它的渲染效果和 JavaScript 引擎能力完全取决于目标机器上装了什么。如果用户用的是 Windows 7 且没装 WebView2那Browser可能直接回退到 IE7 模式ECharts 的 canvas 渲染直接白屏。这是血泪经验别问我怎么知道的。JavaFX 的WebView则自带了一个基于 WebKit 的渲染引擎跟操作系统解耦。只要 JRE 里带了 JavaFX 模块渲染行为就是一致的。代价是 JavaFX 从 JDK 11 开始不再随 JDK 一起发布需要单独引入依赖。另外 JavaFX 的WebView在性能上比原生浏览器内核稍重启动时会有几百毫秒的初始化开销。选型建议很直接如果项目要求跨平台一致性、ECharts 版本较新比如 5.x优先用 JavaFX WebView如果只是 Windows 内部工具、对渲染要求不高SWT Browser 更轻量。2.2 整合方案的整体架构把 JavaFX 嵌进 SWT核心桥梁是JFXPanel。它继承自java.awt.Canvas同时实现了 JavaFX 的初始化逻辑。SWT 这边通过SWT.EMBEDDED和SWT_AWT.new_Frame()拿到一个 AWT 容器再把JFXPanel放进去。整个链路是SWT Composite → SWT_AWT.new_Frame() → AWT Frame → JFXPanel → JavaFX Scene → WebView → HTML/ECharts这条链路里每一层都有自己的线程模型。SWT 要求 UI 操作在 Display 线程AWT 要求在 EDTJavaFX 要求在 JavaFX Application Thread。跨线程调用必须用各自的asyncExec或Platform.runLater否则就是随机崩溃或者界面卡死。2.3 依赖配置与版本对齐用 Maven 的话JavaFX 依赖需要显式声明。以 JavaFX 17 为例dependency groupIdorg.openjfx/groupId artifactIdjavafx-controls/artifactId version17.0.2/version /dependency dependency groupIdorg.openjfx/groupId artifactIdjavafx-web/artifactId version17.0.2/version /dependency dependency groupIdorg.openjfx/groupId artifactIdjavafx-swing/artifactId version17.0.2/version /dependencyjavafx-swing这个包必须加因为JFXPanel就在里面。很多人只加了javafx-controls和javafx-web编译时找不到JFXPanel类然后开始怀疑人生。参数说明version要和 JDK 版本匹配。JDK 11 用 JavaFX 11~17 都行JDK 17 建议用 JavaFX 17 以上。如果项目用的是模块化module-info.java还需要requires javafx.web;和requires javafx.swing;。注意JavaFX 的 WebView 不支持所有现代浏览器特性。比如 Service Worker、WebAssembly 的某些指令集在旧版本 JavaFX 里是缺失的。ECharts 5.x 的 canvas 渲染没问题但如果用了 WebGL 加速的图表类型需要先验证。3. 动手整合从 SWT 容器到 ECharts 渲染的完整链路3.1 初始化 JFXPanel 并嵌入 SWT Composite先看核心代码。这段代码的作用是在一个 SWTComposite里创建JFXPanel并在 JavaFX 线程里初始化WebView加载本地 HTML。import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import javafx.scene.Scene; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import org.eclipse.swt.SWT; import org.eclipse.swt.awt.SWT_AWT; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import java.awt.Frame; public class SwtJfxEchartDemo { public static void main(String[] args) { Display display new Display(); Shell shell new Shell(display); shell.setText(SWT JavaFX ECharts); shell.setSize(1000, 700); shell.setLayout(new FillLayout()); // 创建 SWT 复合容器使用 EMBEDDED 模式支持 AWT 嵌入 Composite composite new Composite(shell, SWT.EMBEDDED | SWT.NO_BACKGROUND); composite.setLayout(new FillLayout()); // 通过 SWT_AWT 桥接拿到 AWT Frame Frame frame SWT_AWT.new_Frame(composite); // JFXPanel 是 JavaFX 与 AWT 的桥梁 JFXPanel jfxPanel new JFXPanel(); frame.add(jfxPanel); frame.setVisible(true); // 必须在 JavaFX Application Thread 上初始化 WebView Platform.runLater(() - { WebView webView new WebView(); WebEngine engine webView.getEngine(); // 加载本地 HTML 文件文件里包含 ECharts 图表 String htmlPath SwtJfxEchartDemo.class .getResource(/web/echart.html).toExternalForm(); engine.load(htmlPath); // 监听加载完成事件方便后续注入数据 engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) - { if (newState javafx.concurrent.Worker.State.SUCCEEDED) { System.out.println(ECharts 页面加载完成); } }); Scene scene new Scene(webView); jfxPanel.setScene(scene); }); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); } }逻辑说明SWT.EMBEDDED是必须的否则SWT_AWT.new_Frame()会抛异常。JFXPanel的setScene必须在 JavaFX 线程里调用所以包在Platform.runLater里。engine.load()接受 URL 字符串本地资源用getResource().toExternalForm()拿到绝对路径。参数说明SWT.NO_BACKGROUND可以避免 SWT 和 AWT 重绘冲突导致的闪烁但不是必须的。如果界面出现黑块加上这个样式通常能解决。3.2 编写 ECharts HTML 页面与数据注入HTML 文件放在src/main/resources/web/echart.html。ECharts 的 JS 文件建议下载到本地不要用 CDN因为桌面端可能没网。!DOCTYPE html html head meta charsetUTF-8 titleEChart/title script srcecharts.min.js/script style html, body { margin: 0; padding: 0; width: 100%; height: 100%; } #chart { width: 100%; height: 100%; } /style /head body div idchart/div script var chart echarts.init(document.getElementById(chart)); var option { title: { text: 示例图表 }, tooltip: { trigger: axis }, xAxis: { type: category, data: [一月,二月,三月,四月] }, yAxis: { type: value }, series: [{ type: bar, data: [120, 200, 150, 80] }] }; chart.setOption(option); // 暴露一个全局函数供 Java 端通过 executeScript 调用 window.updateChart function(jsonData) { var data JSON.parse(jsonData); chart.setOption({ series: [{ data: data.values }] }); }; /script /body /html逻辑说明window.updateChart是留给 Java 端调用的入口。Java 通过engine.executeScript(updateChart(...))把 JSON 数据传进去ECharts 动态刷新。参数说明echarts.init的第二个参数可以指定主题比如dark。如果容器尺寸变化需要调用chart.resize()否则图表不会自适应。3.3 Java 端动态更新图表数据在 Java 代码里拿到WebEngine引用后可以在任意时刻注入数据// 假设 engine 是之前保存的 WebEngine 引用 public void updateChartData(WebEngine engine, ListInteger values) { // 构造 JSON 字符串实际项目建议用 Jackson 或 Gson StringBuilder json new StringBuilder({\values\:[); for (int i 0; i values.size(); i) { json.append(values.get(i)); if (i values.size() - 1) json.append(,); } json.append(]}); // 必须在 JavaFX 线程执行 Platform.runLater(() - { engine.executeScript(updateChart( json.toString() )); }); }逻辑说明executeScript是同步调用但必须在 JavaFX 线程。如果从 SWT 的按钮事件里触发需要先Display.asyncExec再Platform.runLater两层嵌套。参数说明JSON 字符串里的引号需要转义。如果数据量大建议用JSONObject序列化后传入避免手动拼接出错。4. 避坑指南线程、渲染与资源加载的五个翻车现场4.1 现象界面卡死按钮点不动原因在 SWT 的 UI 线程里直接调用了Platform.runLater并等待结果或者反过来在 JavaFX 线程里调用了Display.syncExec。两个线程互相等待死锁。解决所有跨线程调用都用异步方式。SWT 侧用Display.asyncExecJavaFX 侧用Platform.runLater。如果必须等结果用CompletableFuture做超时控制不要无限等待。4.2 现象ECharts 图表白屏控制台无报错原因Browser或WebView加载 HTML 时echarts.min.js路径不对。常见的是用了相对路径./echarts.min.js但 HTML 是通过toExternalForm()加载的基准路径变了。解决把 JS 文件路径改成绝对路径或者在 HTML 里用base标签指定基准。更稳妥的做法是把 ECharts 的 JS 内容直接内联到 HTML 里虽然文件大一点但绝对不会找不到。4.3 现象图表显示但无法交互tooltip 不出现原因JavaFX WebView 默认拦截了一些鼠标事件或者 SWT 的Composite把鼠标事件吞掉了。解决给JFXPanel设置setFocusable(true)并在 SWT 侧确保Composite没有加SWT.TRANSPARENT样式。如果还是不行在 HTML 里给body加onmousemove事件测试确认事件是否到达网页层。4.4 现象程序退出时报线程未结束的警告原因JavaFX 的Platform默认在最后一个窗口关闭时不会自动退出因为JFXPanel不算“窗口”。SWT 的 Display 释放后JavaFX 线程还在跑。解决在shell.dispose()之前调用Platform.exit()并加一个System.exit(0)兜底。如果用了线程池记得shutdownNow。4.5 现象打包成 exe 后 JavaFX 模块找不到原因JDK 11 以上用 jlink 或 jpackage 打包时没有把 JavaFX 的模块加进去。运行时提示Module javafx.web not found。解决用--add-modules javafx.controls,javafx.web,javafx.swing参数启动。如果用的是 jpackage在--add-modules里显式声明。更省事的办法是用org.openjfx:javafx-maven-plugin插件它会自动处理模块路径。5. 进阶技巧用 executeScript 做双向通信与性能验证5.1 从网页回调 Java用 JSObject 桥接WebEngine的executeScript是单向的 Java 调 JS。反过来JS 调 Java 需要用JSObject。在 Java 侧// 在页面加载完成后执行 Platform.runLater(() - { JSObject window (JSObject) engine.executeScript(window); window.setMember(javaBridge, new JavaBridge()); }); // 定义一个桥接类方法名对应 JS 里调用的名字 public class JavaBridge { public void onChartClick(String data) { System.out.println(图表被点击数据 data); // 这里可以回调 SWT 界面更新 } }在 HTML 的 ECharts 配置里加事件监听chart.on(click, function(params) { if (window.javaBridge) { window.javaBridge.onChartClick(JSON.stringify(params.data)); } });逻辑说明setMember把 Java 对象暴露给 JS 的window对象。JS 调用javaBridge.onChartClick时实际执行的是 Java 方法。注意这个方法是在 JavaFX 线程执行的如果要更新 SWT 控件需要Display.asyncExec。参数说明JSObject的setMember必须在页面加载完成后调用否则window对象还没准备好。建议放在LoadWorker的SUCCEEDED回调里。5.2 验证 WebView 是否真的加载成功不要只看界面有没有显示。在LoadWorker里监听异常engine.getLoadWorker().exceptionProperty().addListener((obs, oldEx, newEx) - { if (newEx ! null) { newEx.printStackTrace(); } });另外可以用engine.executeScript(document.title)来验证页面是否可交互。如果返回空字符串或抛异常说明页面没加载完。5.3 一个我常用的性能检查习惯每次集成完我会在 HTML 里加一行console.log(ECharts version: echarts.version)然后在 Java 侧重定向WebEngine的控制台输出engine.setOnAlert(event - System.out.println(JS Alert: event.getData()));虽然setOnAlert只能捕获alert但配合console.log重写也能用。更直接的办法是在executeScript里返回echarts.version字符串打印出来确认版本对不对。从那以后我每次接入新的 ECharts 版本都强制走一遍这个检查避免因为版本不匹配导致某些配置项静默失效。希望帮到你。本文还有配套的精品资源点击获取