I'm currently trying to achieve this with this code:
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
AnchorPane centerPane = new AnchorPane();
Rectangle rect = new Rectangle();
rect.setFill(Color.BLUE);
AnchorPane.setTopAnchor(rect, .25 * centerPane.getHeight());
AnchorPane.setBottomAnchor(rect, .25 * centerPane.getHeight());
AnchorPane.setLeftAnchor(rect, .25 * centerPane.getHeight());
AnchorPane.setRightAnchor(rect, .25 * centerPane.getHeight());
Scene scene = new Scene(centerPane, 320, 240);
stage.setTitle("Anchor Pane Scaling test");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
However I get no rectangle displayed.
I want to know if there's a way to fix this approach or if there's a better way of scaling Nodes inside panes in general that I should try.
There are two reasons your code doesn't work:
Rectangle
(and all Shape
) instances are not resizable. This means a parent (such as AnchorPane
) can not resize these nodes (specifically, Rectangle.resize()
is a no-op).centerPane.getHeight()
before the pane has been laid out. This means its height
(and width
) will be zero at that time. You really need to re-do the layout any time that width and height change.JavaFX supports a collection of Pane
subclasses which manage layout. (See, for example, this tutorial, or the API docs) for details.) Typically, if you require a layout strategy that is not supported by one of these layout panes, then you should define your own by subclassing Region
or Pane
. You should, at a minimum, override the layoutChildren()
method, along with the methods to compute the min/pref/max width and height.
Whether or not your actual use case warrants the amount of coding needed to do this is not clear from your question. However, here is a quick example of a container pane that centers its content and has a configurable value for determining what (linear) proportion of its size its content should occupy.
package org.jamesd.examples.centeringpane;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.layout.Region;
public class CenteringPane extends Region {
private final Node content ;
private DoubleProperty sizeProportion = new SimpleDoubleProperty(1);
public CenteringPane(Node content) {
this.content = content;
getChildren().add(content);
sizeProportion.addListener(obs -> requestLayout());
}
@Override
protected void layoutChildren() {
if (content.isManaged()) {
double w = getWidth();
double h = getHeight();
double targetWidth = (w - snappedLeftInset() - snappedRightInset()) * getSizeProportion();
double targetHeight = (h - snappedTopInset() - snappedBottomInset()) * getSizeProportion();
double x = (w - targetWidth) / 2;
double y = (h - targetHeight) / 2;
content.resizeRelocate(snapPositionX(x), snapPositionY(y), snapSizeX(targetWidth), snapSizeY(targetHeight));
}
}
public double getSizeProportion() {
return sizeProportion.get();
}
public DoubleProperty sizeProportionProperty() {
return sizeProportion;
}
public void setSizeProportion(double sizeProportion) {
this.sizeProportion.set(sizeProportion);
}
@Override
protected double computePrefWidth(double height) {
return snappedLeftInset() + snappedRightInset() + content.prefWidth(height) / getSizeProportion();
}
@Override
protected double computePrefHeight(double width) {
return snappedTopInset() + snappedBottomInset() + content.prefHeight(width) / getSizeProportion();
}
@Override
protected double computeMinWidth(double height) {
return snappedLeftInset() + snappedRightInset() + content.minWidth(height) / getSizeProportion();
}
@Override
protected double computeMinHeight(double width) {
return snappedTopInset() + snappedBottomInset() + content.minHeight(width) / getSizeProportion();
}
@Override
protected double computeMaxWidth(double height) {
return snappedLeftInset() + snappedRightInset() + content.maxWidth(height) / getSizeProportion();
}
@Override
protected double computeMaxHeight(double width) {
return snappedTopInset() + snappedBottomInset() + content.maxHeight(width) / getSizeProportion();
}
@Override
public Orientation getContentBias() {
return content.getContentBias();
}
}
Here is a quick test case:
package org.jamesd.examples.centeringpane;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.layout.Background;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) {
Pane pane = new Pane();
pane.setBackground(Background.fill(Color.BLUE));
CenteringPane centeringPane = new CenteringPane(pane);
BorderPane root = new BorderPane(centeringPane);
Slider slider = new Slider(0, 1, 0.5);
centeringPane.sizeProportionProperty().bind(slider.valueProperty());
HBox controls = new HBox(5, slider);
controls.setPadding(new Insets(10));
controls.setAlignment(Pos.CENTER);
root.setTop(controls);
Scene scene = new Scene(root, 800, 500);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}