开发者

Z-Order in JavaFX

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 various Parents 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.
  • 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:

  • Painter's Algorithm

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 Nodes 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:

  1. 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.

  2. 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 the viewOrder value of each node to the computed distance between that node and the viewer.

The parent will traverse its children in decreasing viewOrder order. This means that a child with a lower viewOrder will be in front of a child with a higher viewOrder. If two children have the same viewOrder, the parent will traverse them in the order they appear in the parent's children list.

However, viewOrder does not alter the layout and focus traversal order of this Node within its parent. A parent always traverses its children 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜