You can, as suggested by James_D, use a GridPane with the appropriate constraints to accomplish this. Here's an example:
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.RowConstraints;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage primaryStage) {
Region region = new Region();
region.setStyle("-fx-background-color: firebrick;");
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.add(region, 0, 0);
ColumnConstraints cc = new ColumnConstraints(0, Region.USE_COMPUTED_SIZE, Double.MAX_VALUE);
cc.setPercentWidth(75);
cc.setHgrow(Priority.ALWAYS);
cc.setHalignment(HPos.CENTER);
grid.getColumnConstraints().add(cc);
RowConstraints rr = new RowConstraints(0, Region.USE_COMPUTED_SIZE, Double.MAX_VALUE);
rr.setPercentHeight(100);
rr.setVgrow(Priority.ALWAYS);
rr.setValignment(VPos.CENTER);
grid.getRowConstraints().add(rr);
primaryStage.setScene(new Scene(grid, 500, 300));
primaryStage.show();
}
}
Another option is to create your own layout which, assuming I didn't make a mistake, is not too difficult:
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
public class CustomPane extends Pane {
// value should be between 0.0 (0%) and 1.0 (100%)
private final DoubleProperty widthRatio =
new SimpleDoubleProperty(this, "widthRatio", 0.75) {
@Override
protected void invalidated() {
requestLayout();
}
};
public final void setWidthRatio(double widthRatio) { this.widthRatio.set(widthRatio); }
public final double getWidthRatio() { return widthRatio.get(); }
public final DoubleProperty widthRatioProperty() { return widthRatio; }
public CustomPane() {}
public CustomPane(double widthRatio) {
setWidthRatio(widthRatio);
}
@Override
protected void layoutChildren() {
double ratio = getWidthRatioClamped();
double x = snappedLeftInset();
double y = snappedTopInset();
double w = getWidth() - snappedRightInset() - x;
double h = getHeight() - snappedBottomInset() - y;
// could do the same thing with the height if you want
x = snapPositionX(x + ((w - w * ratio) / 2.0));
w = snapSizeX(w * ratio);
for (Node child : getManagedChildren()) {
layoutInArea(child, x, y, w, h, -1.0, HPos.CENTER, VPos.CENTER);
}
}
private double getWidthRatioClamped() {
return Math.max(0.0, Math.min(1.0, getWidthRatio()));
}
}
Note that both solutions will take the child nodes' min/pref/max widths and heights into account. If the child node is not resizable (e.g. ImageView, MediaView, etc.) then it won't work as expected. Though you can of course create workarounds for non-resizable nodes. For instance, here's another answer of mine which creates a "resizable ImageView".
Also, note that both solutions layout the children in the area available after the space taken up from any padding and borders is subtracted.