Reputation: 8363
Like what the title suggest, I'm trying to center a TilePane
in its parent container, but at the same time, I want the child nodes of the TilePane
to be placed from 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();
}
This creates a window like this:
Increasing window width causes this:
Continuing to increase window height causes this:
Setting root.setFillWidth(false)
causes:
What can I do to make sure that the whole TilePane
remains centered while the Region
child nodes continues to be placed from left on each row?
To make it more clear, the TilePane
should, during a resize, tries to fit in as many tiles (regions in this example) in all rows except the top row. In other words, there should not be any green space at the right side of the first row. Alternatively, the green space should be equal at both the left and right side. Additionally, the tiles must be placed from left to right. Setting TilePane.setAlignment(Pos.CENTER)
would place any "leftover" tiles at the center of the last row, which is not acceptable.
Upvotes: 2
Views: 1777
Reputation: 82461
Set the preferred size of the tiles via TilePane
. Prevent the parent from resizing the TilePane
beyond it's prefered size by setting fillWidth
to false
and use a listener to the width
property of the VBox
to set the prefColumns
property:
@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();
}
I was amazed by this approach, and I tried something slightly more dynamic.
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);
});
This removes the need to set a static width/height for each tile.
While this works in this basic example, I have yet tried this on more complex tile children.
Upvotes: 3