如何将工具提示添加到JavaFX画布的矩形区域

如何将工具提示添加到JavaFX画布的矩形区域,javafx,tooltip,javafx-8,Javafx,Tooltip,Javafx 8,在我的JavaFX应用程序中,我有一个带有多列的TableView,其中一列以图形形式显示数据。为此,我创建了一个CanvasCell对象,它创建并管理自己的Canvas来处理绘图。绘图部分工作得很好 我现在想在画布/单元格中的一些区域上放置工具提示。每个单元格可能有多个工具提示(这会阻止我在单元格级别添加工具提示),它们应该只在图形的特定区域触发。然而,我根本无法让它正常工作。我似乎不太了解显示节点层次结构的交互作用(请阅读“全部”),因此无法将工具提示放置在实际工作的任何位置 JavaFX的

在我的JavaFX应用程序中,我有一个带有多列的
TableView
,其中一列以图形形式显示数据。为此,我创建了一个
CanvasCell
对象,它创建并管理自己的
Canvas
来处理绘图。绘图部分工作得很好

我现在想在
画布
/
单元格
中的一些区域上放置
工具提示
。每个
单元格可能有多个
工具提示
(这会阻止我在
单元格
级别添加
工具提示
),它们应该只在图形的特定区域触发。然而,我根本无法让它正常工作。我似乎不太了解显示节点层次结构的交互作用(请阅读“全部”),因此无法将
工具提示放置在实际工作的任何位置

JavaFX的文档很少,而Google+在我尝试过的所有搜索中都是空白的。有没有人知道如何做这类事情,或者我现在应该把它当作“不可选择的”一笔勾销

对于信息,CanvasCell
updateItem()
上的扩展
Canvas
对象内调用
draw()
函数。我试图在其中创建
工具提示的代码位于
draw()
函数中,看起来像:

    Rectangle rect = new Rectangle(leftVal, topVal, width, height);
    gc.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
    Tooltip tooltip = new Tooltip("Tooltip Text");
    Tooltip.install(rect, tooltip);
但这段代码写得比其他任何东西都更有希望,在接口中没有生成任何有用的东西


如果有人能给我指出正确的方向,我将非常感激。

我有@Slaw建议的相同解决方案。我的想法是使其更加集中,以便可以传递要显示工具提示的节点及其区域

在下面的演示中,您可以将setToolTips()用作多个节点的静态实用性方法

注意:逻辑的某些部分来自工具提示核心实现;)

导入javafx.animation.animation;
导入javafx.animation.KeyFrame;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.geometry.Bounds;
导入javafx.geometry.Rectangle2D;
导入javafx.scene.Node;
导入javafx.scene.scene;
导入javafx.scene.control.Tooltip;
导入javafx.scene.layout.StackPane;
导入javafx.stage.stage;
导入javafx.util.Duration;
导入java.util.HashMap;
导入java.util.Map;
公共类MultiTooltipDemo扩展了应用程序{
私人双人滑鼠;
私人双人滑鼠;
私有静态int工具提示\u XOFFSET=10;
私有静态int工具提示_YOFFSET=7;
@凌驾
public void start(Stage)引发异常{
StackPane root=新的StackPane();
场景sc=新场景(根,600600);
第二阶段(sc);
stage.show();
StackPane box1=新的StackPane();
框1.setMaxSize(200200);
框1.setStyle(“-fx背景色:红、蓝、黄、绿;-fx背景插图:01100、01000100、10010000100;”);
root.getChildren().add(框1);
映射工具提示=新建HashMap();
工具提示。放置(“我是红色的”,新矩形2D(0,0,100,100));
工具提示。放置(“我是蓝色的”,新矩形2D(100,0,100,100));
工具提示。放置(“我是黄色的”,新矩形2D(01000100));
工具提示。放置(“我是绿色的”,新矩形2D(100100100100));
设置工具提示(框1,工具提示);
}
专用void集合工具提示(节点、贴图工具提示){
持续时间openDelay=持续时间。毫秒(1000);
持续时间hideDelay=持续时间。毫秒(5000);
工具提示工具提示=新工具提示();
Timeline hideTimer=新的Timeline();
getKeyFrames().add(新关键帧(hideDelay));
hideTimer.setOnFinished(事件->{
toolTip.hide();
});
Timeline activationTimer=新时间线();
activationTimer.getKeyFrames().add(新关键帧(openDelay));
activationTimer.setOnFinished(事件->{
Bounds NodeScreenbunds=node.localToScreen(node.getBoundsInLocal());
double nMx=nodeScreenBounds.getMinX();
double nMy=nodeScreenBounds.getMinY();
工具提示。setText(“”);
工具提示。forEach((str,bounds)->{
double mnX=nMx+bounds.getMinX();
double mnY=nMy+bounds.getMinY();
double mxX=mnX+bounds.getWidth();
double mxY=mnY+bounds.getHeight();
如果(lastMouseX>=mnX&&lastMouseX=mnY&&lastMouseY{
双buffPx=2;
double eX=e.getScreenX();
双eY=e.getScreenY();
//显示工具提示时不隐藏鼠标的轻微移动
if(hideTimer.getStatus()==Animation.Status.RUNNING){
if(lastMouseX-buffPx=eX&&lastMouseY-buffPx=eY){
回来
}
}
lastMouseX=e.getScreenX();
lastMouseY=e.getScreenY();
toolTip.hide();
hideTimer.stop();
activationTimer.playFromStart();
});
node.setOnMouseExited(e->{
toolTip.hide();
activationTimer.stop();
hideTimer.stop();
});
}
公共静态void main(字符串[]args){
应用程序启动(args);
}
}

我有与@Slaw建议的解决方案相同的解决方案。我的想法是使其更加集中,以便您可以传递要显示工具提示的节点及其区域

在下面的演示中,您可以将setToolTips()用作多个节点的静态实用性方法

注意:逻辑的某些部分来自工具提示核心实现;)

导入javafx.animation.animation;
导入javafx.animation.KeyFrame;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.geometry.Bounds;
导入javafx.geometry.Rec
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.HashMap;
import java.util.Map;

public class MultiTooltipDemo extends Application {
    private double lastMouseX;
    private double lastMouseY;
    private static int TOOLTIP_XOFFSET = 10;
    private static int TOOLTIP_YOFFSET = 7;

    @Override
    public void start(Stage stage) throws Exception {
        StackPane root = new StackPane();
        Scene sc = new Scene(root, 600, 600);
        stage.setScene(sc);
        stage.show();

        StackPane box1 = new StackPane();
        box1.setMaxSize(200, 200);
        box1.setStyle("-fx-background-color:red, blue, yellow, green; -fx-background-insets: 0 100 100 0, 0 0 100 100, 100 100 0 0, 100 0 0 100;");
        root.getChildren().add(box1);

        Map<String, Rectangle2D> tooltips = new HashMap<>();
        tooltips.put("I am red", new Rectangle2D(0, 0, 100, 100));
        tooltips.put("I am blue", new Rectangle2D(100, 0, 100, 100));
        tooltips.put("I am yellow", new Rectangle2D(0, 100, 100, 100));
        tooltips.put("I am green", new Rectangle2D(100, 100, 100, 100));
        setToolTips(box1, tooltips);

    }

    private void setToolTips(Node node, Map<String, Rectangle2D> tooltips) {
        Duration openDelay = Duration.millis(1000);
        Duration hideDelay = Duration.millis(5000);
        Tooltip toolTip = new Tooltip();

        Timeline hideTimer = new Timeline();
        hideTimer.getKeyFrames().add(new KeyFrame(hideDelay));
        hideTimer.setOnFinished(event -> {
            toolTip.hide();
        });

        Timeline activationTimer = new Timeline();
        activationTimer.getKeyFrames().add(new KeyFrame(openDelay));
        activationTimer.setOnFinished(event -> {
            Bounds nodeScreenBounds = node.localToScreen(node.getBoundsInLocal());
            double nMx = nodeScreenBounds.getMinX();
            double nMy = nodeScreenBounds.getMinY();
            toolTip.setText("");
            tooltips.forEach((str, bounds) -> {
                double mnX = nMx + bounds.getMinX();
                double mnY = nMy + bounds.getMinY();
                double mxX = mnX + bounds.getWidth();
                double mxY = mnY + bounds.getHeight();
                if (lastMouseX >= mnX && lastMouseX <= mxX && lastMouseY >= mnY && lastMouseY <= mxY) {
                    toolTip.setText(str);
                }
            });
            if (!toolTip.getText().isEmpty()) {
                toolTip.show(node.getScene().getWindow(), lastMouseX + TOOLTIP_XOFFSET, lastMouseY + TOOLTIP_YOFFSET);
                hideTimer.playFromStart();
            }
        });

        node.setOnMouseMoved(e -> {
            double buffPx = 2;
            double eX = e.getScreenX();
            double eY = e.getScreenY();
            // Not hiding for slight mouse movements while tooltip is showing
            if (hideTimer.getStatus() == Animation.Status.RUNNING) {
                if (lastMouseX - buffPx <= eX && lastMouseX + buffPx >= eX && lastMouseY - buffPx <= eY && lastMouseY + buffPx >= eY) {
                    return;
                }
            }
            lastMouseX = e.getScreenX();
            lastMouseY = e.getScreenY();
            toolTip.hide();
            hideTimer.stop();
            activationTimer.playFromStart();
        });

        node.setOnMouseExited(e -> {
            toolTip.hide();
            activationTimer.stop();
            hideTimer.stop();
        });
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}
node.setOnMouseMoved(e -> {
    tooltips.forEach((color, bounds) -> {
        if (bounds.contains(e.getX(), e.getY())) {
            tooltip.setText(color.toString());
        }
    });
});
import javafx.application.Application;
import javafx.scene.shape.Rectangle;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.StackPane;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.stage.Stage;

import java.util.HashMap;
import java.util.Map;

/**
 * @see https://stackoverflow.com/a/53785468/230513
 * @see https://stackoverflow.com/a/53753537/230513
 */
public class CanvasTooltipDemo extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        StackPane root = new StackPane();
        Scene sc = new Scene(root, 400, 400);
        stage.setScene(sc);
        Canvas canvas = new Canvas(200, 200);
        root.getChildren().add(canvas);

        Map<Color, Rectangle> tooltips = new HashMap<>();
        tooltips.put(Color.RED, new Rectangle(0, 0, 100, 100));
        tooltips.put(Color.BLUE, new Rectangle(100, 0, 100, 100));
        tooltips.put(Color.YELLOW, new Rectangle(0, 100, 100, 100));
        tooltips.put(Color.GREEN, new Rectangle(100, 100, 100, 100));
        GraphicsContext gc = canvas.getGraphicsContext2D();
        tooltips.forEach((color, bounds) -> {
            gc.setFill(color);
            gc.fillRect(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
        });

        setToolTips(canvas, tooltips);
        stage.show();
    }

    private void setToolTips(Node node, Map<Color, Rectangle> tooltips) {
        Tooltip tooltip = new Tooltip();
        Tooltip.install(node, tooltip);
        node.setOnMouseMoved(e -> {
            tooltips.forEach((color, bounds) -> {
                if (bounds.contains(e.getX(), e.getY())) {
                    tooltip.setText(color.toString());
                }
            });
        });
        node.setOnMouseExited(e -> {
            tooltip.hide();
        });
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}