标题窗格 children 设置 parent 延迟

Titlepane children set the parent delayed

标题窗格的 children 设置他们的 parent(标题窗格)很晚。不像我使用的任何其他 ui 元素。示例代码:

@Override
public void start(Stage primaryStage) throws Exception{
    Group root = new Group();
    Scene scene = new Scene(root, 300, 275);
    primaryStage.setScene(scene);
    primaryStage.show();

    VBox vbox = new VBox(new Circle(30, Color.RED));
    TitledPane titledPane = new TitledPane("circle", vbox);
    root.getChildren().add(titledPane);
    System.out.println(titledPane.getParent());//parent is set
    System.out.println(vbox.getParent());//parent not set
}

虽然 vbox 立即设置了 parent,但标题窗格没有。这种行为是想要的(作为框架的效果)还是真的不一致?

不同之处在于,对于 VBox,您将其放置在 Control 中。它没有添加到实际控件中,而是添加到它的皮肤(或由皮肤创建的结构的一部分)中,并且直到实际物理显示时才构建。例如,如果您将节点设置为标签的图形,则会发生类似的事情:

Label l = new Label();
Rectangle rect = new Rectangle(10, 10);
l.setGraphic(rect);
System.out.println(rect.getParent());

一般规则是,如果您调用

parent.getChildren().add(node);

然后 node.getParent() 将立即设置为 parent。如果调用不同的方法,例如 titledPane.setContent(...)label.setGraphic(...)splitPane.getItems().add(...),则在显示控件之前不会更改节点的父级。

考虑这个更新后的示例,它(我认为)有助于演示正在发生的事情(这已在 James 的回答中进行了解释):

样本的输出是:

Showing an empty scene with no content
Adding some content
Parent of node added directly to a child of a scene graph node: Group@27d5d559[styleClass=root]
Parent of node added only as content to a control: null
Manually requesting CSS application and a layout pass
Detected vbox parent changed to TitledPaneSkin@7cf1d24e[styleClass=content]
Parent of node added only as content to a control after triggering css and layout: TitledPaneSkin@7cf1d24e[styleClass=content]

如您所见,手动触发 CSS 应用程序和布局过程将最终实例化和初始化控件的皮肤,这会将作为内容添加到控件的节点放入场景图和连线中向上节点的父节点。不过,总的来说,请小心手动触发 CSS 和布局通道,因为如果您经常这样做,会对性能产生负面影响 - 在像这个简单示例这样的情况下,性能影响将完全可以忽略不计。


import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class TitledPanSample extends Application {

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

    @Override
    public void start(Stage stage) {
        Group root = new Group();
        Scene scene = new Scene(root, 300, 275);
        stage.setScene(scene);

        System.out.println("Showing an empty scene with no content");
        stage.show();

        System.out.println("Adding some content");

        VBox vbox = new VBox(new Circle(30, Color.RED));
        vbox.parentProperty().addListener((observable, oldValue, newValue) -> {
            System.out.println("Detected vbox parent changed to " + newValue);
        });

        TitledPane titledPane = new TitledPane("circle", vbox);
        root.getChildren().add(titledPane);
        System.out.println("Parent of node added directly to a child of a scene graph node: " + titledPane.getParent());//parent is set
        System.out.println("Parent of node added only as content to a control: " + vbox.getParent());//parent not set

        System.out.println("Manually requesting CSS application and a layout pass");
        root.applyCss();
        root.layout();

        System.out.println("Parent of node added only as content to a control after triggering css and layout: " + vbox.getParent());//parent is set
    }
}