带工具提示的JavaFX菜单(不是MenuItem)?

带工具提示的JavaFX菜单(不是MenuItem)?,javafx,javafx-8,Javafx,Javafx 8,是否有可能向JavaFX(子)菜单添加工具提示 菜单项的常见解决方案(但很难看——为什么菜单不能不仅仅由节点组成?!)是使用CustomMenuItem并在其中放置标签(即节点)——标签可以指定工具提示 但是我如何才能为(子)菜单实现这一点呢?请参见以下示例: import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.stage.St

是否有可能向JavaFX(子)菜单添加工具提示

菜单项的常见解决方案(但很难看——为什么菜单不能不仅仅由节点组成?!)是使用CustomMenuItem并在其中放置标签(即节点)——标签可以指定工具提示

但是我如何才能为(子)菜单实现这一点呢?请参见以下示例:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class CustomSubMenu extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

        MenuButton menuButton = new MenuButton("Menu");

        Label helloLabel = new Label("Hello...");
        helloLabel.tooltipProperty().setValue(new Tooltip("World!"));
        menuButton.getItems().add(new CustomMenuItem(helloLabel));


        Menu submenu = new Menu("This Submenu needs a ToolTip!");
        //             new CustomMenuItem(new Menu()); // doesn't work, because Menu is not a Node.
        submenu.getItems().add(new MenuItem("Some other Item"));
        menuButton.getItems().add(submenu);

        primaryStage.setScene(new Scene(menuButton));
        primaryStage.show();

    }

}
每个菜单项(当然也是一个菜单)都与一个节点相关联。在项目至少显示一次后,可以访问该节点。然后可以通过
item.getStyleableNode()
访问该节点(从fx9开始,有关fx8,请参见下文),并且可以在该节点上设置工具提示

因此,基本上,方法是监听那一瞬间,然后安装一个工具提示。下面的例子是这样做的

  • 为菜单/项创建工具提示并将其放入其属性中
  • 在父菜单上注册onShown处理程序,并安装工具提示(如果可用)
基本片段:

String tooltipKey = "TOOL_TIP";
MenuItem normalItem = new MenuItem("Good .. ");
normalItem.getProperties().put(tooltipKey, new Tooltip("Morning!"));
menuButton.getItems().add(normalItem);
Menu submenu = new Menu("This Submenu needs a ToolTip!");
submenu.getProperties().put(tooltipKey, new Tooltip("It's meee!"));
menuButton.setOnShown(e -> {
    menuButton.getItems().forEach(item -> {
        Node node = item.getStyleableNode();
        if (node != null && item.getProperties().get(tooltipKey) instanceof Tooltip) {
            Tooltip tip = (Tooltip) item.getProperties().get(tooltipKey);
            Tooltip.install(node, tip);
        }
    });

});

对于fx8,基本方法是相同的-但是访问代表menuItem的节点是令人讨厌的(注意:不要在生产中!*cough..):

  • getStyleableNode是fx9的新成员,因此我们必须使用内部api和实现细节进行破解
  • 节点可用的时间更难找到:显而易见的钩子是菜单按钮上显示的事件处理程序,但似乎不受支持(似乎还不受支持-但没有挖掘)
  • 一种方法是先听showingProperty,抓取contextMenu,听它的skinProperty,然后在皮肤设置好后进行安装
代码片段:

// not working - what's wrong?
menuButton.addEventHandler(MenuButton.ON_SHOWN, e -> {
    LOG.info("not getting here?");
    // install tooltips here
});

ChangeListener<Skin> skinListener = (src, ov, skin) -> {
    ContextMenuContent content = (ContextMenuContent) skin.getNode();
    VBox menuBox = (VBox) content.getChildrenUnmodifiable().get(0);
    menuBox.getChildren().forEach(node -> {
        // implementation detail: the menuItem is set in the node's properties
        if (node.getProperties().get(MenuItem.class) instanceof MenuItem) {
            MenuItem item = (MenuItem) node.getProperties().get(MenuItem.class);
            if (node != null && item.getProperties().get(tooltipKey) instanceof Tooltip) {
                Tooltip tip = (Tooltip) item.getProperties().get(tooltipKey);
                Tooltip.install(node, tip);
            }

        }

    });
};
menuButton.showingProperty().addListener((src, ov, nv) -> {
    ContextMenu popup = submenu.getParentPopup();
    if (popup != null) {
        if (popup.getSkin() == null) {
            popup.skinProperty().addListener(skinListener);
        } else {
            popup.skinProperty().removeListener(skinListener);
        }
    }
});
//不工作-怎么了?
menuButton.addEventHandler(显示menuButton.ON,e->{
LOG.info(“不到这里?”);
//在此处安装工具提示
});
ChangeListener skinListener=(src、ov、skin)->{
ContextMenuContent content=(ContextMenuContent)skin.getNode();
VBox menuBox=(VBox)content.getChildrenUnmodifiable().get(0);
menuBox.getChildren().forEach(节点->{
//实现细节:menuItem在节点的属性中设置
if(node.getProperties().get(MenuItem.class)instanceof MenuItem){
MenuItem项=(MenuItem)node.getProperties().get(MenuItem.class);
if(node!=null&&item.getProperties().get(tooltipKey)instanceof Tooltip){
工具提示提示=(工具提示)item.getProperties().get(工具提示键);
工具提示。安装(节点、提示);
}
}
});
};
menuButton.showingProperty().addListener((src,ov,nv)->{
ContextMenu popup=子菜单.getParentPopup();
如果(弹出!=null){
if(popup.getSkin()==null){
popup.skinProperty().addListener(skinListener);
}否则{
popup.skinProperty().RemovelListener(skinListener);
}
}
});

我想出了一个简单的方法来欺骗菜单项作为控件标签节点,使用如下代码:

    Label lb=new Label("MenuItem Text");
    lb.setStyle("-fx-text-fill:black;");
    MenuItem myMenuItem = new MenuItem(null, lb);
    Tooltip tips = new Tooltip("Your tip text here");   
    Tooltip.install(myMenuItem.getGraphic(), tips);

将工具提示设置为标签,这对我来说非常有用。

谢谢你,克利奥帕特拉!但似乎getStyleableNode是用Java9添加的——不幸的是,我现在只能用Java8了……对于菜单,同样的技巧也可以用。i、 e.使用菜单mu=新菜单(空,新标签(“菜单名”))。