除非有菜单项,否则无法单击菜单



我正在尝试使用 javafx 在菜单栏中的菜单上的单击事件上添加一个操作。 问题是,我看到了很多关于它的帖子,但没有答案对我有用。 我设法使用菜单上的"显示"来做到这一点,这很好,但是如果菜单至少有一个菜单项,则此事件仅触发(与其他事件一样(。 这不是我想要的,但我现在别无选择。

这是 fxml :

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.HangmanGameFXViews.view.MenuesActionsControlleur">
<top>
<MenuBar fx:id="menusBar" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" onMouseReleased="#switchToAbout" prefWidth="400.0" BorderPane.alignment="CENTER">
<menus>
<Menu mnemonicParsing="false" text="Fichiers">
<items>
<MenuItem mnemonicParsing="false" text="Nouveau" />
<MenuItem mnemonicParsing="false" onAction="#switchToScore" text="Scores" />
<MenuItem mnemonicParsing="false" onAction="#switchToRules" text="Règles" />
<MenuItem mnemonicParsing="false" onAction="#exit" text="Quitter" />
</items>
</Menu>
<Menu fx:id="about" mnemonicParsing="false" onShowing="#switchToAbout" text="À propos">
<items>
<!--                THIS THE DUMMY MENU I USE TO BE ABLE TO TRIGGER THE EVENT ON THE PARENT -->
<MenuItem fx:id="dummyMenuItem" mnemonicParsing="false" />
</items></Menu>
</menus>
</MenuBar>
</top>
</BorderPane>

视图控制器的代码:

package org.HangmanGameFXViews.view;
import org.HangmanGameFXViews.Main;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.stage.Stage;
public class MenuesActionsControlleur {
private Stage stageDialogue;
private Main main;
@FXML
private Menu about;
@FXML
private MenuBar menusBar;
@FXML
private MenuItem dummyMenuItem;
@FXML
private void initialize() {
about.addEventHandler(Event.ANY, new EventHandler<Event>() {
@Override
public void handle(Event event) {
System.out.println("Showing Menu 1");
System.out.println(event.getTarget().toString());
System.out.println(event.getEventType().toString());
}
});
}
@FXML
public void switchToRules() {
main.switchToRules();
}
@FXML
public void switchToAbout() {
dummyMenuItem.setDisable(true);
main.switchToAbout();
dummyMenuItem.setDisable(false);
}
@FXML
public void clickableMenu(ActionEvent e){
System.out.println("Menu clicked");
}
@FXML
public void switchToNew() {
main.switchToNew();
}
@FXML
public void switchToScore() {
main.switchToScore();
}
public Stage getStageDialogue() {
return stageDialogue;
}
public void setStageDialogue(Stage stageDialogue) {
this.stageDialogue = stageDialogue;
}
@FXML
public void exit() {
stageDialogue.close();
}
public void setMainClass(Main m) {
main = m;
stageDialogue = main.getStagePrincipal();
}
}

感谢您的任何帮助。

正如评论中已经指出的:菜单并不意味着像按钮(或菜单项(一样 - 它的"动作"是打开显示其项目的上下文菜单。在该上下文中实现它以触发是可能的(请注意:这可能会使用户感到困惑,并且在使用实现细节和内部 API 时需要一些污垢(。

也就是说:安装自定义处理程序的基本思想是在表示 Menu 的 styleableNode 上执行此操作。默认情况下,仅当 Menu 是 ContextMenu 中的一个项目时,才会实现对该节点的访问,而不是在 MenuBar 中的 MenuButton 表示时实现(我认为这是一个错误,但那是另一回事(。

所以我们必须自己做

  • 扩展菜单
  • 添加 API 以设置包含菜单栏
  • 实现getStyleableNode以访问MenuButton
  • 添加一些接线以重定向鼠标释放(或其他任何内容(以触发菜单的操作

自定义菜单可能是这样的(显然不是生产质量:):

public class MyMenu extends Menu {
private MenuBar parentMenuBar;
private Parent menuBarContainer;
private MenuButton menuButton;
private EventHandler<MouseEvent> redirector = this::redirect;
public MyMenu(String string) {
super(string);
}
public void setParentMenuBar(MenuBar menuBar) {
this.parentMenuBar = menuBar;
// tbd: cleanup if menuBar and/or its skin disposed/changed 
if (menuBar != null) {
menuBar.skinProperty().addListener((src, ov, nv) -> {
if (nv instanceof MenuBarSkin && menuBar.getChildrenUnmodifiable().size() == 1) {
menuBarContainer = (Parent) menuBar.getChildrenUnmodifiable().get(0);
menuBarContainer.getChildrenUnmodifiable().addListener((ListChangeListener)c -> {
updateEventRedirector();
});
updateEventRedirector();
}
});
}
}
protected void redirect(MouseEvent e) {
// fire only if there are no items 
if (getItems().size() == 0) fire();
}
/**
* Rewire eventHandler when our styleable node is changed
*/
private void updateEventRedirector() {
if (menuButton != null) {
menuButton.removeEventHandler(MouseEvent.MOUSE_RELEASED, redirector);
}
menuButton = getParentMenuButton();
if (menuButton != null) {
menuButton.addEventHandler(MouseEvent.MOUSE_RELEASED, redirector);
}
}
@Override
public Node getStyleableNode() {
if (parentMenuBar != null && parentMenuBar.getChildrenUnmodifiable().size() == 1) {
return menuButton;
}
return super.getStyleableNode();
}
private MenuButton getParentMenuButton() {
if (parentMenuBar == null || parentMenuBar.getChildrenUnmodifiable().size() != 1) return null;
// beware: implementation detail of menuBarSkin!
Parent parent = (Parent) parentMenuBar.getChildrenUnmodifiable().get(0);
for (Node child : parent.getChildrenUnmodifiable()) {
// beware: internal api!
if (child instanceof MenuBarButton) {
MenuBarButton menuButton = (MenuBarButton) child;
if (menuButton.menu == this) {
return menuButton;
}
}
}
return null;
}
}

要使用:

bar = new MenuBar();
first = new MyMenu("dummy");
first.setParentMenuBar(bar);
first.setOnAction(e -> System.out.println("menu handler"));
bar.getMenus().addAll(first, new Menu("other"));

最新更新