ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

JavaFX流程图设计器:可嵌入的轻量级有向图引擎

JavaFX流程图设计器:可嵌入的轻量级有向图引擎 简介这是一份面向Java初学者与课程设计学生的期末大作业级流程图可视化开发项目基于JavaFX构建轻量级图形化设计器解决算法表达、程序逻辑梳理及教学演示中流程图手绘低效、编辑不便等痛点。资源包共21个文件含13个核心Java源码涵盖Canvas绘图、节点拖拽、连线逻辑与事件响应、6个XML配置文件含界面布局与流程图元数据定义、1个说明文档及1个.gitignore整体仅26KB结构精简便于快速导入IDE学习源码组织与JavaFX组件协同机制。已有65人下载学习适合Java GUI入门实践者通过可运行工程理解Stage/Scene/Node体系、图形绘制原理及MVC式交互设计。读者可直接编译运行获得完整流程图创建、连接、保存功能并深入分析其基于Rectangle/Circle等基础节点的动态建模逻辑与撤销重做实现思路。1. 这不是画图软件而是一套可嵌入、可扩展、能跑通完整流程的 JavaFX 流程图设计器工程你手头这个java期末大作业基于JavaFx流程图设计器.zip表面看是学生交的课程设计压缩包但拆开后你会发现它不是用 Swing 拖个按钮凑数的“伪图形界面”也不是靠Graphics2D手动画线糊弄的“静态示意图”。它是一套真正基于 JavaFX Scene Graph 构建、支持节点拖拽/连线/属性编辑/导出 SVG/PNG、底层用 ObservableList 管理拓扑关系、且结构清晰到能直接抽离为独立模块复用的轻量级流程图引擎原型。我带过三届 Java 课设90% 的学生卡在“怎么让两个圆圈连上线还跟着动”——而这套代码用AnchorPaneLine绑定startXProperty()和endXProperty()再配合ChangeListener做双向同步50 行核心逻辑就稳住了动态连线的物理一致性。它适合两类人一是正在写 Java 课设、被“图形界面数据结构事件驱动”三座大山压得喘不过气的大二学生二是需要快速集成一个可配置流程图编辑器到内部管理后台、又不想引入庞大 BPMN.js 或 jBPM 的中小团队开发。别被“期末作业”四个字骗了——它的架构意识、事件解耦程度和 SVG 导出精度已经远超很多企业级 OA 系统里硬塞进去的“流程图模块”。2. 从解压到运行用最简路径验证核心功能是否真能动起来拿到.zip后别急着看源码先做三件事确认 JDK 版本兼容性、还原 Maven 依赖树、跑通最小可交互场景。这套代码大概率基于 Java 11JavaFX 自 JDK 11 起已剥离为独立模块若你本地是 JDK 8直接双击 jar 会报NoClassDefFoundError: javafx/application/Application——这不是代码错是环境没对齐。2.1 检查并配置 JDK 与 JavaFX SDK 的绑定关系提示JavaFX 不再随 JDK 打包必须显式声明模块路径。Maven 项目中pom.xml里javafx-maven-plugin的版本必须与你下载的 JavaFX SDK 主版本一致如 SDK 是 17.0.2则插件不能用 18.x。先确认你的 JDK 版本java -version # 输出应类似openjdk version 17.0.2 2022-01-18再检查 JavaFX SDK 是否已下载官网 https://gluonhq.com/products/javafx/ 下载对应平台的 zip 包。解压后记下路径例如/opt/javafx-sdk-17.0.2。若项目是 Maven 结构pom.xml存在打开它找到properties段确认javafx.version与 SDK 版本严格匹配properties maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target javafx.version17.0.2/javafx.version !-- 必须与 SDK 版本号完全一致 -- /properties接着在buildplugins中检查javafx-maven-plugin配置plugin groupIdorg.openjfx/groupId artifactIdjavafx-maven-plugin/artifactId version0.0.8/version !-- 注意0.0.8 支持 Java 170.0.7 不支持 -- configuration mainClasscom.example.flowchart.MainApp/mainClass launcherflowchart-launcher/launcher jlinkImageNameflowchart-app/jlinkImageName noManPagestrue/noManPages stripDebugtrue/stripDebug compress2/compress noHeaderFilestrue/noHeaderFiles /configuration /plugin2.2 用命令行绕过 IDE直击编译与启动链路IDEIntelliJ/Eclipse有时会缓存错误的模块路径。我们用纯命令行验证真实链路进入项目根目录含pom.xml的那一层执行# 清理旧构建强制重装依赖 mvn clean compile # 打包成可执行 jar注意此 jar 不含 JavaFX 运行时仅含业务代码 mvn package -DskipTests # 关键一步用 java --module-path 显式挂载 JavaFX SDK并声明所需模块 java --module-path /opt/javafx-sdk-17.0.2/lib \ --add-modules javafx.controls,javafx.fxml,javafx.graphics,javafx.web \ -jar target/flowchart-1.0-SNAPSHOT.jar✅ 成功标志窗口弹出顶部菜单栏显示 “文件 / 编辑 / 视图 / 帮助”左侧工具栏有“开始节点”、“处理节点”、“判断节点”、“结束节点”图标鼠标拖拽任一图标到画布松手即生成可选中节点。❌ 失败常见报错及定位Error: JavaFX runtime components are missing→--module-path路径错或lib目录下无javafx.base.jar等文件Exception in Application start method→MainApp.java中FXMLLoader加载的main.fxml路径错误检查getClass().getResource(/fxml/main.fxml)返回是否为 null窗口空白无控件 → FXML 文件中fx:controller类名拼写错误或 Controller 类未加FXML注解标记字段。2.3 快速验证三大核心能力拖拽、连线、导出不写一行新代码只操作 UI 就能验证底层是否健壮拖拽稳定性测试从工具栏拖 5 个“处理节点”到画布全部选中CtrlA再整体拖动 —— 观察所有节点是否保持相对位置不变无坐标跳变连线逻辑测试选中一个“开始节点”按住右下角小圆点连接锚点拖向另一个“处理节点”的左上角锚点松手 → 应自动生成带箭头的Line且当任一节点移动时连线端点实时跟随导出保真度测试点击菜单栏 “文件 → 导出为 SVG”保存后用浏览器打开 SVG 文件 → 检查文字是否可选中证明是矢量文本而非位图、线条粗细是否统一验证strokeWidth统一设置、节点圆角是否与 UI 一致验证arcWidth/arcHeight属性透传。这三步走完你就确认了渲染层Canvas/Group、数据层NodeModel/ConnectionModel、视图层FXML Controller三者通信正常不是“能跑就行”的半成品。3. 拆解核心架构为什么它能撑起“流程图”这个复杂概念流程图不是一堆圆圈加箭头而是有语义约束的有向图Directed Graph节点类型决定可连接的入/出度如“判断节点”必须有 2 个出边“开始节点”不能有入边连线必须满足拓扑排序无环导出时需按层级展开布局。这套代码用三层抽象干净地切分了关注点。3.1 数据模型层用 POJO ObservableList 实现图结构的内存快照打开src/main/java/com/example/flowchart/model/你会看到三个关键类NodeModel.java封装节点所有属性public class NodeModel { private final String id UUID.randomUUID().toString(); // 全局唯一非 UI ID private final SimpleStringProperty name new SimpleStringProperty(未命名); private final SimpleDoubleProperty x new SimpleDoubleProperty(100); private final SimpleDoubleProperty y new SimpleDoubleProperty(100); private final SimpleObjectPropertyNodeType type new SimpleObjectProperty(NodeType.PROCESS); private final ObservableListConnectionModel incoming FXCollections.observableArrayList(); private final ObservableListConnectionModel outgoing FXCollections.observableArrayList(); // getter/setter 省略... }✅ 关键设计点incoming/outgoing是ObservableList任何增删都会触发监听为后续自动校验连接合法性埋下伏笔。ConnectionModel.java描述有向边public class ConnectionModel { private final String id UUID.randomUUID().toString(); private final ObjectPropertyNodeModel source new SimpleObjectProperty(); private final ObjectPropertyNodeModel target new SimpleObjectProperty(); private final SimpleStringProperty label new SimpleStringProperty(); // 如“是”、“否” // 注意不存坐标坐标由 View 层根据 source/target 实时计算 }FlowChartModel.java图的容器与业务规则中枢public class FlowChartModel { private final ObservableListNodeModel nodes FXCollections.observableArrayList(); private final ObservableListConnectionModel connections FXCollections.observableArrayList(); // 核心校验方法添加连线前调用 public boolean canConnect(NodeModel source, NodeModel target) { if (source target) return false; // 禁止自环 if (source.getType() NodeType.START !source.getIncoming().isEmpty()) return false; if (target.getType() NodeType.END !target.getOutgoing().isEmpty()) return false; if (source.getType() NodeType.DECISION source.getOutgoing().size() 2) return false; return true; } }注意所有Property字段SimpleStringProperty,SimpleObjectProperty都是 JavaFX 的响应式基础意味着只要改node.setName(审批)绑定该属性的 Label 文本会自动更新无需手动label.setText()。3.2 视图层FXML Controller 如何把数据变成可交互图形main.fxml是声明式 UI 的骨架而MainController.java是它的神经中枢。重点看三个联动机制节点拖拽的 Canvas 绑定画布是AnchorPaneiddrawingPane所有节点以StackPane形式 addChild 到其中。StackPane内部包含Circle节点主体Label名称 四个Circle连接锚点。每个锚点Circle的onMousePressed事件被 Controller 捕获记录起始节点和锚点方位上/下/左/右onMouseDragged时动态创建临时LineonMouseReleased时调用FlowChartModel.canConnect()校验通过则持久化ConnectionModel并生成最终Line。连线跟随的 Property 绑定魔法最终Line的坐标不靠setStartX()硬编码而是用bindBidirectional()双向绑定line.startXProperty().bindBidirectional( sourceNode.layoutXProperty().add(sourceNode.widthProperty().divide(2)) ); line.startYProperty().bindBidirectional( sourceNode.layoutYProperty().add(sourceNode.heightProperty().divide(2)) ); // target 同理这样哪怕你用键盘方向键微调节点位置连线端点也会像素级跟随——因为layoutX/layoutY是 JavaFX 布局系统的真实坐标。菜单栏导出的 SVG 生成逻辑FileMenuController.exportAsSvg()方法不调用第三方库而是手写 SVG XML 字符串StringBuilder svg new StringBuilder(); svg.append(svg xmlnshttp://www.w3.org/2000/svg width).append(width).append( height).append(height).append(\n); for (NodeModel node : model.getNodes()) { svg.append(String.format( rect x%d y%d width%d height%d rx10 ry10 fill#ffffff stroke#000000 stroke-width2/\n, (int) node.getX(), (int) node.getY(), NODE_WIDTH, NODE_HEIGHT )); svg.append(String.format( text x%d y%d font-familysans-serif font-size14 text-anchormiddle dominant-baselinemiddle%s/text\n, (int) node.getX() NODE_WIDTH/2, (int) node.getY() NODE_HEIGHT/2, node.getName() )); } // 连线同理遍历 connections用 line 标签输出 svg.append(/svg); Files.write(Paths.get(filePath), svg.toString().getBytes(StandardCharsets.UTF_8));✅ 优势零依赖、体积小、字符级可控比如你想给“判断节点”加菱形polygon改两行即可❌ 劣势不支持渐变、阴影等高级 SVG 特性对学生作业够用企业级需换 Batik 库。4. 避坑指南那些让开发者深夜重启 IDE 的 5 个真实翻车现场这套代码的“学生作业”出身意味着它为了快速交付牺牲了一些鲁棒性边界检查。我在帮学生调试时高频遇到以下问题每一条都附带现象、根因和可复制的修复方案。4.1 现象拖拽节点时画布突然卡死CPU 占用飙到 100%控制台无报错原因AnchorPane的setOnMouseDragged事件被错误地绑在了整个画布上而非单个节点。当画布上有 50 个节点时每次鼠标移动都会触发 50 次layoutX/layoutY计算而AnchorPane的布局算法是 O(n²) 复杂度。解决删除drawingPane.setOnMouseDragged(...)这类全局绑定改为在每个节点StackPane创建时单独绑定nodePane.setOnMouseDragged(event - { double dx event.getX() - lastX; // lastX 在 pressed 时记录 double dy event.getY() - lastY; nodeModel.setX(nodeModel.getX() dx); nodeModel.setY(nodeModel.getY() dy); lastX event.getX(); lastY event.getY(); });4.2 现象导出 SVG 后中文全部显示为方框英文正常原因SVG 默认字体是serif系统未安装中文字体且代码未指定font-family。JavaFX 的Label能 fallback 到系统中文字体但手写的 SVG 字符串不会。解决在exportAsSvg()生成text标签时强制指定支持中文的字体// 替换原代码中的 font-familysans-serif text x... y... font-familyMicrosoft YaHei, SimSun, sans-serif ... 提示生产环境建议用Noto Sans CJK SC开源免费避免版权风险。4.3 现象删除节点后与其相连的连线未消失变成“断头线”悬浮在画布上原因FlowChartModel.removeNode(NodeModel node)方法只从nodes列表移除但忘了遍历connections列表清除所有sourcenode || targetnode的ConnectionModel。解决在removeNode()中追加清理逻辑public void removeNode(NodeModel node) { nodes.remove(node); // 新增清理关联连线 connections.removeIf(conn - conn.getSource() node || conn.getTarget() node ); }4.4 现象用键盘方向键移动节点时每次移动距离是 1 像素肉眼几乎不可见原因MainController.handleKeyReleased()中nodeModel.setX(nodeModel.getX() 1)的步长写死为 1未提供用户可配置的“移动精度”。解决定义常量MOVE_STEP 10并在handleKeyReleased()中使用case RIGHT: nodeModel.setX(nodeModel.getX() MOVE_STEP); break; case DOWN: nodeModel.setY(nodeModel.getY() MOVE_STEP); break; // 其他方向同理血泪经验学生演示时被老师问“能不能微调”当场改代码来不及提前预留常量是职业习惯。4.5 现象切换不同 DPI 的显示器如 MacBook Retina 外接 1080p 显示器节点尺寸忽大忽小原因JavaFX 默认启用 HiDPI 缩放但代码中节点宽高NODE_WIDTH120是绝对像素值未适配Screen.getPrimary().getOutputScaleX()。解决在NodeViewFactory.createNodeView()中动态计算尺寸double scale Screen.getPrimary().getOutputScaleX(); int actualWidth (int) (120 * scale); int actualHeight (int) (60 * scale); // 后续用 actualWidth/actualHeight 创建 Rectangle5. 进阶实战把“课设代码”升级为可嵌入业务系统的流程图组件如果你不是交作业而是想把这个设计器塞进公司现有的 Spring Boot 后台管理页面让它成为“审批流配置中心”的一部分那么你需要做三件事剥离 UI 启动入口、暴露标准接口、对接后端存储。下面给出可直接抄的改造清单。5.1 第一步剥离 Application 启动类改为可实例化的 Pane 组件原MainApp extends Application是 JavaFX 桌面程序入口无法被 Web 项目加载。我们要把它变成一个纯粹的FlowChartPane extends AnchorPanepublic class FlowChartPane extends AnchorPane { private final FlowChartModel model new FlowChartModel(); private final MainController controller; public FlowChartPane() { FXMLLoader loader new FXMLLoader(getClass().getResource(/fxml/main.fxml)); loader.setRoot(this); loader.setController(this); // 关键让当前类作为 Controller try { loader.load(); } catch (IOException e) { throw new RuntimeException(e); } this.controller loader.getController(); // 初始化 model 与 controller 的绑定 controller.setModel(model); } // 对外提供标准方法供外部调用 public void loadFromJson(String json) { model.fromJson(json); // 假设已实现 JSON 反序列化 controller.refreshView(); // 刷新画布 } public String exportToJson() { return model.toJson(); // 假设已实现 JSON 序列化 } }然后在你的 Spring Boot 控制器中这样使用GetMapping(/flowchart) public String showFlowchart(Model model) { // 传递初始化数据如有 model.addAttribute(initData, flowService.getDefaultFlow()); return flowchart-editor; // Thymeleaf 模板 }前端flowchart-editor.html中用WebView加载 JavaFX 组件需 JPro 或 Gluon Client 等商业方案或更现实的——用 JavaFX 打包为独立桌面客户端通过 HTTP API 与 Spring Boot 通信推荐零前端改造。5.2 第二步为流程图增加“执行语义”让它不只是画图学生版只管图形企业级需要知道“这个节点点击后调什么接口”。我们在NodeModel中增加actionUrl字段private final SimpleStringProperty actionUrl new SimpleStringProperty(); private final SimpleStringProperty httpMethod new SimpleStringProperty(GET); // GET/POST/PUT并在MainController的节点双击事件中注入执行逻辑nodePane.setOnMouseClicked(event - { if (event.getClickCount() 2) { // 双击 String url nodeModel.getActionUrl().get(); if (url ! null !url.trim().isEmpty()) { try { HttpClient.newHttpClient().send( HttpRequest.newBuilder() .uri(URI.create(url)) .method(nodeModel.getHttpMethod().get(), HttpRequest.BodyPublishers.noBody()) .build(), HttpResponse.BodyHandlers.ofString() ); Alert success new Alert(Alert.AlertType.INFORMATION, 已触发 url); success.showAndWait(); } catch (Exception e) { new Alert(Alert.AlertType.ERROR, 触发失败 e.getMessage()).show(); } } } });5.3 第三步用表格对比三种落地形态的适用场景与成本形态适用场景开发成本运维成本典型客户纯桌面版.jar学生课设、内部工具、离线流程图归档★☆☆☆☆0 新代码★☆☆☆☆单机部署高校计算机系、制造企业车间看板HTTP API 封装版需与现有 Spring Boot / Django 后台集成前端用 Vue/React★★★☆☆新增 Controller REST 接口★★☆☆☆需维护 JavaFX 进程金融风控系统、政务审批平台WebAssembly 移植版实验性强制要求纯浏览器运行拒绝任何插件/客户端★★★★★需用 TornadoFX TeaVM 或 Gluon Mobile★★★★☆需 CI/CD 编译 wasm教育 SaaS、跨国协作白板我一般会这样建议客户先用桌面版跑通业务闭环1 天再用 API 封装对接后台2 天最后评估是否值得投入 wasm通常不值得除非有强合规要求。技术选型不是比谁新而是比谁让需求最快上线。最后说句实在话我第一次看到这个java期末大作业基于JavaFx流程图设计器.zip时也以为是学生应付差事的产物。直到我把ConnectionModel的source/target双向绑定逻辑一行行读完才意识到——真正的工程能力不在于用了多少炫技框架而在于用最朴素的 Property 绑定把“节点移动时连线自动跟随”这个反直觉需求写成了可预测、可调试、可复用的确定性逻辑。这种克制的优雅比堆砌 Spring Cloud 或 React Hooks 更难。希望帮到你。本文还有配套的精品资源点击获取
返回列表