如何将工具提示添加到 JavaFX 的矩形区域 Canvas
How do I add a Tooltip to a rectangular region of a JavaFX Canvas
在我的 JavaFX 应用程序中,我有一个包含多列的 TableView
,其中一列以图形形式显示数据。为此,我创建了一个 CanvasCell
对象,该对象创建并管理自己的 Canvas
来处理绘图。绘图部分效果很好。
我现在想将 Tooltips
放在 Canvas
/Cell
中的某些区域。每个 Cell
可能有多个 Tooltips
(这阻止我在 Cell
级别添加 Tooltip
)并且它们应该只在图表的特定区域触发。但是,我根本无法让它发挥作用。我似乎不太了解显示节点层次结构的交互(阅读 "at all"),无法将 Tooltip
放置在它实际工作的任何地方。
JavaFX 的文档很少,Google + SO 对于我尝试过的所有搜索结果都是空白的。有没有人知道如何做这种事情,或者我现在应该把它写成 "not an option"。
有关信息,CanvasCell
在 updateItem()
上的扩展 Canvas
对象中调用了一个 draw()
函数。我试图创建 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);
但该代码的编写更多是出于希望而不是其他任何东西,并且不会在界面中生成任何有用的东西。
如果有人能指出正确的方向,我将不胜感激。
根据@Slaw 的建议,我有相同的解决方案。我的想法是让它更加集中,这样你就可以通过你的节点和你想要显示工具提示的区域。
在下面的演示中,您可以使用 setToolTips() 作为多个节点的静态实用方法。
注:部分逻辑引用自Tooltip核心实现;)
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);
}
}
如果您不需要图示的时序控制, you can simply install the Tooltip
on the enclosing Canvas
and leverage Shape::contains
来调节文本,如下所示。
node.setOnMouseMoved(e -> {
tooltips.forEach((color, bounds) -> {
if (bounds.contains(e.getX(), e.getY())) {
tooltip.setText(color.toString());
}
});
});
按照建议 here, Java 9 and later provide control over Tooltip
通过属性 showDelay
和 showDuration
计时。
针对 Swing 说明了类似的方法。
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
* @see
*/
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);
}
}
在我的 JavaFX 应用程序中,我有一个包含多列的 TableView
,其中一列以图形形式显示数据。为此,我创建了一个 CanvasCell
对象,该对象创建并管理自己的 Canvas
来处理绘图。绘图部分效果很好。
我现在想将 Tooltips
放在 Canvas
/Cell
中的某些区域。每个 Cell
可能有多个 Tooltips
(这阻止我在 Cell
级别添加 Tooltip
)并且它们应该只在图表的特定区域触发。但是,我根本无法让它发挥作用。我似乎不太了解显示节点层次结构的交互(阅读 "at all"),无法将 Tooltip
放置在它实际工作的任何地方。
JavaFX 的文档很少,Google + SO 对于我尝试过的所有搜索结果都是空白的。有没有人知道如何做这种事情,或者我现在应该把它写成 "not an option"。
有关信息,CanvasCell
在 updateItem()
上的扩展 Canvas
对象中调用了一个 draw()
函数。我试图创建 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);
但该代码的编写更多是出于希望而不是其他任何东西,并且不会在界面中生成任何有用的东西。
如果有人能指出正确的方向,我将不胜感激。
根据@Slaw 的建议,我有相同的解决方案。我的想法是让它更加集中,这样你就可以通过你的节点和你想要显示工具提示的区域。
在下面的演示中,您可以使用 setToolTips() 作为多个节点的静态实用方法。
注:部分逻辑引用自Tooltip核心实现;)
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);
}
}
如果您不需要图示的时序控制Tooltip
on the enclosing Canvas
and leverage Shape::contains
来调节文本,如下所示。
node.setOnMouseMoved(e -> {
tooltips.forEach((color, bounds) -> {
if (bounds.contains(e.getX(), e.getY())) {
tooltip.setText(color.toString());
}
});
});
按照建议 here, Java 9 and later provide control over Tooltip
通过属性 showDelay
和 showDuration
计时。
针对 Swing
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
* @see
*/
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);
}
}