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

时间:2018-12-12 10:20:46

标签: javafx tooltip javafx-8

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

我现在想将Tooltips放在Canvas / Cell中的某些区域上。每个Tooltips可能有多个Cell(这使我无法在Tooltip级别添加Cell),它们只能在图形的特定区域触发。但是,我根本无法使其正常运行。我似乎不太了解显示节点层次结构的相互作用(请阅读“全部”内容),无法将Tooltip放置在实际起作用的任何地方。

JavaFX的文档稀疏,对于我尝试过的所有搜索,Google + SO都为空白。有没有人知道如何做这种事情,或者我现在应该把它写成“不是一种选择”。

有关信息,CanvasCelldraw()的扩展Canvas对象内调用updateItem()函数。我尝试创建Tooltip的代码位于该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);

但是该代码的编写比其他任何事情都希望更多,并且不会在界面中产生任何有用的信息。

如果有人可以指出正确的方向,我将不胜感激。

2 个答案:

答案 0 :(得分:1)

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

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

注意:部分逻辑是从工具提示核心实现中引用的;)

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);
    }
}

答案 1 :(得分:1)

如果您不需要here所示的时序控制,只需在封闭的Tooltip上安装Canvas并利用Shape::contains来调节文本,如图所示在下面。

node.setOnMouseMoved(e -> {
    tooltips.forEach((color, bounds) -> {
        if (bounds.contains(e.getX(), e.getY())) {
            tooltip.setText(color.toString());
        }
    });
});

如建议的here一样,Java 9和更高版本通过属性showDelayshowDuration提供了对Tooltip时序的控制。

here展示了用于Swing的类似方法。

image

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);
    }
}