How do I set Z-order for VBox
in JavaFX?
Z-order in JavaFX is actually the order in the scenegraph, eg. in the content sequence of the scene (then in the contents of the groups, containers, etc.).
All nodes have also toFront() and toBack() functions to help changing this order. For finer control, you have to remove nodes from one place and insert it higher or lower in the sequence.
TL;DR:
The order of the scene graph determines the rendering order (Z-order) of the nodes.
- This order is determined by the
children
list of the variousParent
s in the scene. - Change this order to change the rendering order.
- Warning: Changing this order may have undesired side effects if a layout (e.g.,
VBox
) uses said order to determine where children are positioned relative to each other.
- This order is determined by the
In JavaFX 9+ you can use the
Node#viewOrder
property to change the render order of nodes without changing the layout positions of those nodes.
Painter's Algorithm
The rendering of the scene graph follows the:
The painter’s algorithm creates images by sorting the polygons within the image by their depth and placing each polygon in order from the farthest to the closest object
The name "painter's algorithm" refers to the technique employed by many painters where they begin by painting distant parts of a scene before parts that are nearer, thereby covering some areas of distant parts
The actual implementation may differ from a traditional painter's algorithm, for performance purposes, and also for depth buffer handling for 3D scenes, but the general principle for rendering remains the same and is a good framework to use when reasoning about what will be seen, and what will be occluded.
The rendering order input to the algorithm is first the z coordinate of a node in relation to the camera, then the view order if set, then the child order for elements with the same z coordinate and unspecified view order.
Z-Order in JavaFX
In JavaFX, Z-order is primarily determined by the order of the scene graph. And that order is determined by the order of the Node
s in each Parent
's children list (recursively, depth-first).
For example, the following application:
import javafx.application.Application;
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.VBox;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage primaryStage) {
var titleLabel = new Label("Title");
var fieldLabel = new Label("Field:");
var textField = new TextField();
var hbox = new HBox(fieldLabel, textField);
var button = new Button("Action");
var root = new VBox(titleLabel, hbox, button);
var scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Yields the following scene graph:
+-------------+
| |
| Window |
| |
+------|------+
|
+------|------+
| |
| Scene |
| |
+------|------+
|
+------|------+
| |
+----------- VBox ------------+
| | | |
| +------|------+ |
| | |
+-------|-----+ +------|------+ +------|------+
| | | | | |
| Label | | HBox | | Button |
| | | | | |
+-------------+ +------|------+ +-------------+
|
+---------|---------+
| |
+------|------+ +-------|-----+
| | | |
| Label | | TextField |
| | | |
+-------------+ +-------------+
The VBox
has three children: a Label
, an HBox
, and a Button
. If these children were to overlap, then the Label
would be rendered behind the HBox
and all its descendants; similarly, the HBox
(and all its descendants) would be rendered behind the Button
. For the HBox
, if its children were to overlap, then the second Label
would be rendered behind the TextField
.
So, if you want to change the Z-order of nodes then you just need to change the order of the scene graph. In the context of a single parent, there are two convenience methods you can use:
Node#toBack()
: Moves the node to the start of its parent's children list, causing it to be rendered behind the other children of that parent.Node#toFront()
: Moves the node to the end of its parent's children list, causing it to be rendered on top of the other children of that parent.
Warning: Many layouts, such as VBox
, use the order of the children list to determine where to position its children relative to each other. Changing the order of the children list of these layouts will change the position of the children in addition to the render order.
FXML
Note that in FXML, the declaration order of child node elements is the order they will appear in their parent's children list.
The Node.viewOrder
Property (JavaFX 9+)
As noted in the warning above, changing the order of the children list can have undesired side effects. To avoid these side effects, you can use the Node#viewOrder
property.
Defines the rendering and picking order of this
Node
within its parent.This property is used to alter the rendering and picking order of a node within its parent without reordering the parent's
children
list. For example, this can be used as a more efficient way to implement transparency sorting. To do this, an application can assign theviewOrder
value of each node to the computed distance between that node and the viewer.The parent will traverse its
children
in decreasingviewOrder
order. This means that a child with a lowerviewOrder
will be in front of a child with a higherviewOrder
. If two children have the sameviewOrder
, the parent will traverse them in the order they appear in the parent'schildren
list.However,
viewOrder
does not alter the layout and focus traversal order of thisNode
within its parent. A parent always traverses itschildren
list in order when doing layout or focus traversal.
The default value of this property is 0.0
.
Note this property only affects the rendering order of a single parent. For instance, in the above example, you couldn't force the TextField
to be rendered on top of the Button
by only changing the viewOrder
value of the TextField
, because the Button
and TextField
belong to different parents.
This property can be set from CSS with -fx-view-order
.
3D Scenes
Every Node
has x
, y
, and z
coordinates/dimensions. In 2D scenes, only the x
and y
values have any meaning. But in 3D scenes the z
coordinate/dimension also has meaning. To have a 3D scene you must instantiate a Scene
(or SubScene
) with its depthBuffer
parameter set to true
.
Scene scene = new Scene(root, width, height, true, SceneAntialiasing.BALANCED);
- See documentation.
This can only be done during construction. The depthBuffer
value cannot be changed after. Also, note the SceneAntialiasing
argument is optional (i.e., there's an overload that doesn't have that parameter, but it defaults to DISABLED
).
With the toFront() and toBack() functions you can indeed influence the z-order, but be aware that this also influences the layout. The HBox and VBox for instance also use the sequence of children to do the layout and moving something to the front will also move it to the end of the [HV]Box. This might not be what you are looking for.
I was looking for a way to do an animation with the animated Node on top of all others, without messing up the layout. There seems to be no way to do that because the z-order and layout order are both taken from the child-order.
© 2022 - 2024 — McMap. All rights reserved.