JavaFX 如何使工具提示在节点上居中?

JavaFX How Can I Center a Tooltip on a Node?

我目前正在开发一种表单,该表单具有在将焦点从一个节点更改为另一个节点时进行验证的功能,并希望在包含错误的节点上方居中显示工具提示(如果存在错误)。我正在使用工具提示的 show(Node ownerNode, double anchorX, double anchorY) 方法签名来指定我想将其附加到哪个节点以及将其放置在何处。

我试过以下代码:

Tooltip tooltip = new Tooltip("Error"); 
tooltip.show(node, 0, 0);
double tooltipMiddle = tooltip.getWidth() / 2;
tooltip.hide(); 
double nodeMiddle = node.getWidth() / 2; 

//Gets the node's x and y position within the window
Bounds bounds = node.localToScene(node.getBoundsInLocal());  

//Tooltip's bottom-right corner is set to the anchor points, so I set x to
//the node's x coordinate plus half the width of the node plus half the width
//of the tooltip 
tooltip.show(node,
             bounds.getMinX() + nodeMiddle + tooltipMiddle, bounds.getMinY());

这让我非常接近中心,但它仍然关闭。我在互联网上到处寻找帮助,但我就是找不到任何帮助,所以我想我应该在这里问一下。
我有没有机会深入了解为什么我无法按照我喜欢的方式进行工作?

首先你应该使用 Bounds bounds = node.localToScreen(node.getBoundsInLocal()); 因为可能不清楚相关的 Scene 是什么。

然后调用 tooltip.show(node, bounds.getMinX(), bounds.getMinY()); 应该会给你一个工具提示,其左上角与节点的左上角相同。

现在,tooltip.show(node, bounds.getMinX() + nodeMiddle - tooltipMiddle, bounds.getMinY() - tooltipHeight); 应该会产生预期的结果,

double tooltipMiddle = (tooltip.getWidth()-18) / 2;
double tooltipHeight = tooltip.getHeight()-18;
double nodeMiddle = getWidth() / 2;

18 来自一些小实验,我不确定为什么宽度和高度会偏离那个数量,但它似乎与工具提示中文本的长度或行数无关。

我带来的代码提供了工具提示的正确定位,但远非完美。带来全面的解决方案需要大量工作(如果你愿意,我们可以讨论)。 到底我认为你有一个数学问题和 Tooltip class 可能不是最好的选择。

import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class TooltipApp extends Application {

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

    @Override
    public void start(Stage stage) throws Exception {
        TextField textField = new TextField();
        textField.setPrefWidth(100.);

        Button button = new Button("Tooltip");

        HBox hBox = new HBox(textField, button);
        hBox.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);

        Label label = new Label("Empty!");
        label.setVisible(false);
        Pane tooltipPane = new Pane(label);
        tooltipPane.setMouseTransparent(true);

        StackPane stackPane = new StackPane(hBox, tooltipPane);
        stackPane.setPrefSize(600., 400.);

        Scene scene = new Scene(stackPane);

        stage.setScene(scene);
        stage.show();

        button.setOnMouseClicked(event -> {
            if (label.isVisible()) {
                label.setVisible(false);
            } else {
                Bounds sceneBounds = textField.localToScene(textField.getBoundsInLocal());
                label.setLayoutX((sceneBounds.getMinX() + (sceneBounds.getWidth() / 2)) - (label.getWidth() / 2));
                label.setLayoutY(sceneBounds.getMinY() - label.getHeight());
                label.setVisible(true);
            }
        });
    }
}