JavaFX:如何将 TilePane 居中但将 TilePane children 从 left-to-right 放置?

JavaFX: How to center a TilePane but place the TilePane children from left-to-right?

如标题所示,我试图将 TilePane 置于其 parent 容器的中心,但与此同时,我想要 child 节点 TilePane 从 left-to-right.

放置
@Override
public void start(final Stage primaryStage) throws Exception {
    final VBox root = new VBox();
    final Scene sc = new Scene(root);
    primaryStage.setScene(sc);
    
    final TilePane tp = new TilePane();
    root.getChildren().add(tp);
    
    tp.setPrefColumns(3);
    tp.setAlignment(Pos.TOP_LEFT);
    tp.setStyle("-fx-background-color: green;");
    root.setAlignment(Pos.CENTER);
    root.setFillWidth(true);

    Stream.iterate(0, i -> i + 1).limit(5).forEach(i -> {
        Region r = new Region();
        r.setPrefSize(200, 200);
        r.setStyle("-fx-background-color: red; -fx-border-color: blue; -fx-border-width: 1;");

        tp.getChildren().add(r);
    });

    primaryStage.show();
}

这会创建一个像这样的 window:

增加 window 宽度会导致:

继续增加 window 高度会导致:

设置 root.setFillWidth(false) 导致:

我该怎么做才能确保整个 TilePane 保持居中,同时 Region child 节点继续从左侧放置在每一行上?

更新

为了更清楚,TilePane 应该在调整大小时尝试在除顶行之外的所有行中适应尽可能多的图块(本例中的区域)。也就是说,第一行右边不应该有任何绿色的space。或者,绿色 space 应该在左侧和右侧相等。此外,瓷砖必须从左到右放置。设置 TilePane.setAlignment(Pos.CENTER) 会将任何“剩余”图块放在最后一行的中心,这是不可接受的。

通过 TilePane 设置磁贴的首选大小。通过将 fillWidth 设置为 false 并使用监听器 width 属性 的 VBox 设置 prefColumns 属性:

@Override
public void start(Stage primaryStage) {
    final VBox root = new VBox();
    final Scene sc = new Scene(root);
    primaryStage.setScene(sc);

    final TilePane tp = new TilePane();
    tp.setPrefTileWidth(200);
    tp.setPrefTileHeight(200);
    root.getChildren().add(tp);

    tp.setPrefColumns(3);
    tp.setAlignment(Pos.TOP_LEFT);
    tp.setStyle("-fx-background-color: green;");
    root.setAlignment(Pos.CENTER);
    root.setFillWidth(false);

    // set prefColumns from a listener instead of a binding
    // to prevent the initial value from being set to 0
    root.widthProperty().addListener((o, oldValue, newValue) -> {
        // allow as many columns as fit the parent but keep it in
        // range [1, childCount]
        tp.setPrefColumns(Math.min(tp.getChildren().size(),
                Math.max(1, (int) (newValue.doubleValue() / tp.getPrefTileWidth()))));
    });

    Stream.iterate(0, i -> i + 1).limit(5).forEach(i -> {
        Region r = new Region();
        r.setStyle("-fx-background-color: red; -fx-border-color: blue; -fx-border-width: 1;");

        tp.getChildren().add(r);
    });

    primaryStage.show();
}

OP 更新

我对这种方法感到惊讶,我尝试了一些更动态的方法。

root.widthProperty().addListener((o, oldValue, newValue) -> {
    double maxWidth = tp.getChildren()
                        .stream()
                        .filter(n -> n instanceof Region)
                        .map(n -> ((Region) n).getWidth())
                        .max(Double::compareTo)
                        .orElse(0d);

    tp.setPrefColumns(Math.min(tp.getChildren().size(),
            Math.max(1, (int) (newValue.doubleValue() / maxWidth))));
});

Stream.iterate(0, i -> i + 1).limit(5).forEach(i -> {
    Region r = new Region();
    Random random = new Random();
    r.setPrefSize(random.nextInt(150) + 50, random.nextInt(150) + 50);
    System.out.println(r.getPrefWidth());
    System.out.println(r.getPrefHeight());
    r.setStyle("-fx-background-color: red; -fx-border-color: blue; -fx-border-width: 1;");

    tp.getChildren().add(r);
});

这样就无需为每个图块设置静态 width/height。

虽然这在这个基本示例中有效,但我已经在更复杂的图块子项上尝试过。