Linux下JavaFX调用右键菜单后事件检测异常问题求助
Linux下JavaFX选框拖拽异常问题(Java 17+JavaFX 21)
问题现象
- 我开发的JavaFX图形编辑器主区域为带滚动条的
ScrollPane,内容是包含图形层和选择层的AnchorPane,图形层绑定了右键菜单 - 正常状态下,选框拖拽到Stage外时仍能触发拖拽事件,选框会扩展,滚动条自动调整
- 打开右键菜单后,再使用选框时,拖拽事件仅在
ScrollPane可视区域内有效,无法扩展到Stage外;鼠标按下事件可被检测,但Stage外的拖拽事件完全无响应 - 若打开右键菜单后先点击图形层,或按ESC键,选框拖拽功能恢复正常
- 该问题仅在Linux系统出现,Windows下运行正常;从Java 8内置JavaFX迁移到Java 17+JavaFX 21后出现此问题
- 目前已用Robot API模拟ESC键作为临时解决方案,希望找到更合理的修复方式
最小可复现代码
package org.example; import javafx.application.Application; import javafx.geometry.Point2D; import javafx.scene.Scene; import javafx.scene.control.ContextMenu; import javafx.scene.control.MenuItem; import javafx.scene.control.ScrollPane; import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; public class Main extends Application { public static void main(String[] args) { launch(args); } public void start(Stage primaryStage) { AnchorPane mainPane = new AnchorPane(); // 创建滚动面板 ScrollPane scroll = new ScrollPane(); scroll.setContent(mainPane); scroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); scroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); scroll.setPrefWidth(1024); scroll.setPrefHeight(768); // 创建选框图层 AnchorPane selectionLayer = new AnchorPane(); selectionLayer.setMouseTransparent(true); // 创建图形图层 BorderPane graphicLayer = new BorderPane(); graphicLayer.setMinHeight(768); graphicLayer.setMinWidth(1024); graphicLayer.setFocusTraversable(false); // 创建右键菜单 ContextMenu contextMenu = new ContextMenu(); contextMenu.getItems().add(new MenuItem("test1")); contextMenu.getItems().add(new MenuItem("test2")); graphicLayer.setOnContextMenuRequested(e -> contextMenu.show(graphicLayer, e.getScreenX(), e.getScreenY())); SelectionController controller = new SelectionController(selectionLayer); graphicLayer.setOnMousePressed(event -> { System.err.println("PRESSED"); if (contextMenu.isShowing()) { contextMenu.hide(); } controller.startSelection(event); }); graphicLayer.setOnMouseDragged(event -> { System.err.println("DRAGGED"); controller.setPosition(event); }); graphicLayer.setOnMouseReleased(event -> { System.err.println("RELEASED"); controller.finishSelection(event); }); mainPane.getChildren().addAll(graphicLayer, selectionLayer); Scene scene = new Scene(scroll); primaryStage.setScene(scene); primaryStage.show(); } class SelectionController { AnchorPane selectionLayer; Point2D startingPoint; Rectangle selectionFrame; SelectionController(AnchorPane selectionLayer) { this.selectionLayer = selectionLayer; } void startSelection(MouseEvent event) { startingPoint = new Point2D(event.getX(), event.getY()); selectionFrame = new Rectangle(); selectionFrame.setId("selection_frame"); selectionFrame.setFill(Color.TRANSPARENT); selectionFrame.getStrokeDashArray().add(10.0); selectionFrame.setStrokeWidth(2.0); selectionFrame.setStroke(Color.LIGHTSKYBLUE); selectionFrame.xProperty().set(startingPoint.getX()); selectionFrame.yProperty().set(startingPoint.getY()); selectionLayer.getChildren().add(selectionFrame); } void setPosition(MouseEvent event) { double x = event.getX(); double y = event.getY(); if (x < startingPoint.getX()) { selectionFrame.xProperty().set(x); selectionFrame.widthProperty().set(startingPoint.getX() - x); } else { selectionFrame.widthProperty().set(x - startingPoint.getX()); } if (y < startingPoint.getY()) { selectionFrame.yProperty().set(y); selectionFrame.heightProperty().set(startingPoint.getY() - y); } else { selectionFrame.heightProperty().set(y - startingPoint.getY()); } } void finishSelection(MouseEvent event) { // 筛选图形图层内选框中的对象逻辑 selectionLayer.getChildren().clear(); } } }
重现步骤
- 启动应用
- 创建选框:在Stage内点击左上角,拖拽鼠标到Stage外,可看到拖拽事件正常触发,选框扩展
- 右键点击唤起ContextMenu
- 菜单显示时重复步骤2,选框无法超出Stage边界,Stage外无拖拽事件触发
- 再次右键唤起菜单,先点击Stage内区域或按ESC键,再创建选框,功能恢复正常
内容的提问来源于stack exchange,提问作者vit
相关产品推荐
相关产品推荐

