0

通过 a 在 Swing 窗口中嵌入菜单时JFXPanel,我无法通过单击来关闭菜单。有时它会闪烁,好像它关闭并立即重新打开。

package testjavafx;

public class TestMenuJavaFX extends Application {

    @Override
    public void start(Stage primaryStage) {
        MenuBar menuBar = new MenuBar(
                new Menu("Menu 1", null,
                        new MenuItem("Menu item 1-1"),
                        new MenuItem("Menu item 1-2")),
                new Menu("Menu 2", null,
                        new MenuItem("Menu item 2-1"),
                        new MenuItem("Menu item 2-2")),
                new Menu("Menu 3", null,
                        new MenuItem("Menu item 3-1"),
                        new MenuItem("Menu item 3-1")));
        menuBar.setPrefWidth(300);
        Region root = new Pane(menuBar);
        root.setPrefSize(300, 185);

        useJFXPanel(root);
        //usePrimaryStage(primaryStage, root);
    }

    private static void useJFXPanel(Region root) {
        JFXPanel jfxPanel = new JFXPanel();
        jfxPanel.setScene(new Scene(root));
        JFrame jFrame = new JFrame("test menu JavaFX");
        jFrame.setSize((int) root.getWidth(), (int) root.getHeight());
        jFrame.add(jfxPanel);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.setLocationRelativeTo(null);
        jFrame.setVisible(true);
    }

    private static void usePrimaryStage(Stage primaryStage, Parent root) {
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(TestMenuJavaFX.class, args);
    }
}

通过使用该方法usePrimaryStage,我得到了预期的行为(单击菜单将其打开,再次单击以将其关闭),但useJFXPanel出现了问题。

这是一个事件处理问题,鼠标单击首先作为 Swing 鼠标事件分派到 JFXPanel,然后在JFXPanel内部将 JavaFX 鼠标事件分派到其嵌入的Scene.
看来,在 Swing 部分,菜单失去焦点并关闭,当事件到达 Menu 实例时,它发现它已关闭并因此打开它。

我尝试继承Menu该类以向其添加鼠标单击事件处理程序,但是它不处理鼠标单击,并且使用提供的显示/显示和隐藏/隐藏事件无济于事(因为问题发生得更早)。
我还尝试子类MenuBar化以添加鼠标单击事件处理程序,但是仅在单击菜单外的栏时才调用该处理程序,所以这里没有运气,并且子类化JFXPanel以通过反射黑魔法覆盖processMouseEvent和检索MenuBarButton实例但我不能不要让它工作。

这是一个错误,对吧?是否有(简单而干净,理想情况下)解决此问题的方法?

我正在使用 OpenJDK 11.0.10.9 和 JavaFX 17.0.0.1。

4

1 回答 1

4

在我的系统上,它不会运行,而是在启动时挂起,因为它JFrame是在错误的线程上创建和显示的。更正确实显示了您描述的行为,这似乎是一个错误。

我找到了一种解决方法,即捕获ON_HIDING事件并安排调用以在事件队列的下方隐藏菜单,使用Platform.runLater(...). 生成的代码如下所示;

public class TestMenuJavaFX extends Application {

    @Override
    public void start(Stage primaryStage) {
        MenuBar menuBar = new MenuBar(
                new Menu("Menu 1", null,
                        new MenuItem("Menu item 1-1"),
                        new MenuItem("Menu item 1-2")),
                new Menu("Menu 2", null,
                        new MenuItem("Menu item 2-1"),
                        new MenuItem("Menu item 2-2")),
                new Menu("Menu 3", null,
                        new MenuItem("Menu item 3-1"),
                        new MenuItem("Menu item 3-1")));
        menuBar.setPrefWidth(300);
        
        menuBar.getMenus().forEach(menu -> {
            menu.addEventHandler(Menu.ON_HIDING, e -> {
                Platform.runLater(menu::hide);
            });
        });
        
        Region root = new Pane(menuBar);
        root.setPrefSize(300, 185);

        useJFXPanel(root);
        //usePrimaryStage(primaryStage, root);
    }

    private static void useJFXPanel(Region root) {
        JFXPanel jfxPanel = new JFXPanel();
        jfxPanel.setScene(new Scene(root));
        SwingUtilities.invokeLater(() -> {
            JFrame jFrame = new JFrame("test menu JavaFX");
            jFrame.setSize((int) root.getWidth(), (int) root.getHeight());
            jFrame.add(jfxPanel);
            jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            jFrame.setLocationRelativeTo(null);
            jFrame.setVisible(true);
        });
    }

    private static void usePrimaryStage(Stage primaryStage, Parent root) {
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(TestMenuJavaFX.class, args);
    }
}
于 2022-01-24T17:24:34.783 回答