Javafx自定义工具提示实现仅在上次安装的节点上显示

Javafx自定义工具提示实现仅在上次安装的节点上显示,java,javafx,javafx-8,Java,Javafx,Javafx 8,我正在尝试创建自己的工具提示实现,它有一个箭头,并在安装它的节点的某一侧显示工具提示。 看起来是这样的: 这是我到目前为止写的代码 Tooltip.java 但是,这只适用于安装了工具提示的最后一个节点,如果我将鼠标悬停在具有工具提示的其他节点上,光标将开始闪烁,并且工具提示根本不显示。问题是什么?建议:为了帮助别人更好地回答你的问题,考虑创建一个以较短格式演示你的问题的方法。谢谢,我应该包括琐事吗?比如创建应用程序类、控制器和视图?如果没有,你认为我可以在这里添加什么?这不是你真正可以添加的

我正在尝试创建自己的工具提示实现,它有一个箭头,并在安装它的节点的某一侧显示工具提示。 看起来是这样的:

这是我到目前为止写的代码

Tooltip.java


但是,这只适用于安装了工具提示的最后一个节点,如果我将鼠标悬停在具有工具提示的其他节点上,光标将开始闪烁,并且工具提示根本不显示。问题是什么?

建议:为了帮助别人更好地回答你的问题,考虑创建一个以较短格式演示你的问题的方法。谢谢,我应该包括琐事吗?比如创建应用程序类、控制器和视图?如果没有,你认为我可以在这里添加什么?这不是你真正可以添加的;目前,你的帖子包含了很多大多数人都不想阅读的代码。mcve的目的是缩短您的问题,这样人们就可以花更少的时间阅读代码,花更多的时间想出解决方案。您已经标记了您的帖子[javafx-8]和[javafx-2]。我假设您没有同时使用这两个版本,因此请删除您没有使用的版本的标记。2). 对于光标开始闪烁的节点,请检查
MOVE\u处理程序
之后是否立即调用
KILL\u处理程序
。工具提示可能出现在节点顶部,导致鼠标“移出”节点。“我应该包括一些琐碎的东西吗?比如创建应用程序类、控制器和视图?”是的,是MCVE中的C。如果我们不能复制粘贴并运行它(并看到问题),它就不是MCVE。
public class Tooltip extends PopupControl {

    private StringProperty message = new SimpleStringProperty();
    private final static TooltipSkin skin = new TooltipSkin();
    private final static TooltipBehavior behavior = new TooltipBehavior();

    public Tooltip(Node node, String message) {
        this.message.setValue(message);
        if (getContent() != skin.createSkin()) {
            getContent().setAll(skin.createSkin());
        }
        behavior.install(node, Tooltip.this);
    }

    /****************************************************************
     **********                  ACCESSORS               ************
     ****************************************************************/

    String getMessage() {
        return message.get();
    }

    StringProperty messageProperty() {
        return message;
    }

    private static class TooltipBehavior {
        final String TOOLTIP_PROP_KEY = "fxtooltip";

        private void install(Node node, Tooltip tooltip) {
            if (node == null)
                return;
            node.addEventHandler(MouseEvent.MOUSE_ENTERED, ENTERED_HANDLER);
            node.addEventHandler(MouseEvent.MOUSE_EXITED, KILL_HANDLER);
            node.addEventHandler(MouseEvent.MOUSE_PRESSED, KILL_HANDLER);
            node.getProperties().put(TOOLTIP_PROP_KEY, tooltip);
        }

        private EventHandler<MouseEvent> ENTERED_HANDLER = (MouseEvent event) -> {
            Node hoveredNode = (Node) event.getSource();
            Tooltip tooltip = ((Tooltip) hoveredNode.getProperties().get(TOOLTIP_PROP_KEY));
            String message = tooltip.getMessage();
            skin.setMessage(message);
            tooltip.show(getWindow(hoveredNode), 0, 0);
        };
        private EventHandler<MouseEvent> KILL_HANDLER = (MouseEvent event) -> {
            Node hoveredNode = (Node) event.getSource();
            Tooltip tooltip = ((Tooltip) hoveredNode.getProperties().get(TOOLTIP_PROP_KEY));
            tooltip.hide();
        };

        private Window getWindow(final Node node) {
            final Scene scene = node == null ? null : node.getScene();
            return scene == null ? null : scene.getWindow();
        }
    }

    private static class TooltipSkin {
        private static HBox $RootView = new HBox();
        private static StackPane $PaneMessageBody = new StackPane();
        private static StackPane $PaneArrow = new StackPane();
        private static Text $TextMessage = new Text();
        private static boolean skinInitialized = false;

        Pane createSkin() {
            if (skinInitialized) {
                return $RootView;
            } else {
                skinInitialized = true;
                $RootView.getChildren().addAll($PaneMessageBody, $PaneArrow);
                $PaneMessageBody.getChildren().addAll($TextMessage);
            }
            return $RootView;
        }

        void setMessage(String message) {
            $TextMessage.setText(message);
        }
    }
}    
new Tooltip($BtnDownload, "Download");
new Tooltip($BtnActivate, "Activate");