You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

重现步骤

  1. 启动应用
  2. 创建选框:在Stage内点击左上角,拖拽鼠标到Stage外,可看到拖拽事件正常触发,选框扩展
  3. 右键点击唤起ContextMenu
  4. 菜单显示时重复步骤2,选框无法超出Stage边界,Stage外无拖拽事件触发
  5. 再次右键唤起菜单,先点击Stage内区域或按ESC键,再创建选框,功能恢复正常

内容的提问来源于stack exchange,提问作者vit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 18:40:54