I have Text
nodes in HBox
and I need to know the exact height of the row before the row is shown (without using pulse listener). This is my test code:
public class Test1 extends Application {
@Override
public void start(Stage primaryStage) {
Font systemFont = Font.font("System", 14);
VBox root = new VBox(
createBox("red", systemFont),
createBox("yellow", systemFont),
createBox("red", systemFont)
);
Scene scene = new Scene(root, 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
private HBox createBox(String color, Font font) {
var hBox = new HBox();
hBox.setStyle("-fx-padding: 0; -fx-background-color: " + color);
var text = new Text("Hello, World!");
text.setFont(font);
hBox.getChildren().add(text);
hBox.heightProperty().addListener((ov, oldV, newV) -> System.out.println("NewHeight:" + newV));
return hBox;
}
public static void main(String[] args) {
launch(args);
}
}
This is the result stage:
And this is the output:
NewHeight:17.0
NewHeight:17.0
NewHeight:17.0
As you see, the font size is 14, while the row height is 17, so, we have +3 pixels difference for every row.
Could anyone say how to calculate this difference (before the row is shown) or at least how to remove it - I can add it manually via padding.
The documentation of Font
says the size is in points, not pixels:
The size of a
Font
is described as being specified in points which are a real world measurement of approximately1/72
inch.
Which means having a font with size 14
does not necessarily mean the height of a Text
will be the same value. But you can get the height of a Text
before it's shown via its layout bounds.
Text text = new Text("Hello, World!");
text.setFont(Font.font("System", 14));
double height = text.getLayoutBounds().getHeight();
System.out.println(height);
Note the height will also be affected by the Text.boundsType
property. By default, that property will be set to TextBoundsType.LOGICAL
.
If you want the height of something like an HBox
, which contains the Text
, before its shown then you can try and make use of methods like Node::autoside()
, Node::prefHeight(double)
, Node::applyCss()
(requires the Node
to be part of a Scene
), Parent::layout()
, and so on. However, those methods aren't guaranteed to give the actual height the node when displayed. The size of a node depends on many things:
The minimum, preferred, and maximum sizes of the node and whether the node is resizable. Note the size values may be rounded for and within regions if Region.snapToPixel
is true (the default).
Property configurations of the Node
, its ancestors, and its descendants. These configurations can come from code/FXML or CSS. This includes all sources of CSS (application's user-agent stylesheet, scene's user-agent stylesheet, scene stylesheets, parent stylesheets, inline style).
The rest of the scene graph, including how the node's Parent
decides to layout said node.
Depending on what you really want, the following may be enough:
HBox box = new HBox(text); // 'text' from previous snippet
box.autosize();
System.out.println(box.getHeight());
Or you may need to add your HBox
to a Scene
, call applyCss()
and layout()
on the root, then get the height. But to be truly accurate in this case, you'd basically need to recreate the real scene graph. You might as well just add the HBox
to the real Scene
and get its height after a layout pulse at that point. You can simply observe the Region.height
or Node.layoutBounds
property if you always need the most up-to-date value. If there are transformations involved (e.g., scaling) and you want the visual height of a node, then observe the Node.boundsInParent
property.
That said, it's not often one needs to calculate the height of an arbitrary node ahead of time. Perhaps there's another way to do what you want. For instance, if you're trying to create your own layout then don't try to perform size calculations from outside your layout. Instead, subclass Region
(or similar) and override the layoutChildren()
method. Perform the size calculations in that method.