I would like to display a grid containing a various number of rectangles in JavaFX. It is important that this grid cannot be resized.
I chose the GridPane
layout. I dynamically add javafx.scene.shape.Rectangle
to it. Here's how my grid looks like with 2 rows and 4 columns.
Upon resizing, I would like it to keep the same overall shape, that is to say each Rectangle
having the same size and keeping an horizontal and vertical gaps between my Rectangle
s.
However, here's what I get with a 4x4 grid:
The problems being:
- The last row and last column do not have the same size as the rest of the
Rectangle
s. - The gaps have disappeared.
Here is my code responsible for refreshing the display:
public void refreshConstraints() {
getRowConstraints().clear();
getColumnConstraints().clear();
for (int i = 0; i < nbRow; i++) {
RowConstraints rConstraint = new RowConstraints();
// ((nbRow - 1) * 10 / nbRow) = takes gap into account (10% of height)
rConstraint.setPercentHeight(100 / nbRow - ((nbRow - 1) * 10 / nbRow));
getRowConstraints().add(rConstraint);
}
for (int i = 0; i < nbColumn; i++) {
ColumnConstraints cConstraint = new ColumnConstraints();
cConstraint.setPercentWidth(100 / nbColumn - ((nbColumn - 1) * 10 / nbColumn));
getColumnConstraints().add(cConstraint);
}
}
Using the setFillWidth
and setHgrow
yields no result either, the gap is kept between my Rectangle
s, but the Rectangle
s aren't resized and they overlap the rest of my GUI elements.
EDIT: MCVE code:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class DynamicGrid extends Application {
//Class containing grid (see below)
private GridDisplay gridDisplay;
@Override
public void start(Stage primaryStage) {
//Represents the grid with Rectangles
gridDisplay = new GridDisplay(400, 200);
//Fields to specify number of rows/columns
TextField rowField = new TextField();
TextField columnField = new TextField();
//Function to set an action when text field loses focus
buildTextFieldActions(rowField, columnField);
HBox fields = new HBox();
fields.getChildren().add(rowField);
fields.getChildren().add(new Label("x"));
fields.getChildren().add(columnField);
BorderPane mainPanel = new BorderPane();
mainPanel.setLeft(gridDisplay.getDisplay());
mainPanel.setBottom(fields);
Scene scene = new Scene(mainPanel);
primaryStage.setTitle("Test grid display");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void buildTextFieldActions(final TextField rowField, final TextField columnField) {
rowField.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (!t1) {
if (!rowField.getText().equals("")) {
try {
int nbRow = Integer.parseInt(rowField.getText());
gridDisplay.setRows(nbRow);
gridDisplay.updateDisplay();
} catch (NumberFormatException nfe) {
System.out.println("Please enter a valid number.");
}
}
}
}
});
columnField.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (!t1) {
if (!columnField.getText().equals("")) {
try {
int nbColumn = Integer.parseInt(columnField.getText());
gridDisplay.setColumns(nbColumn);
gridDisplay.updateDisplay();
} catch (NumberFormatException nfe) {
System.out.println("Please enter a valid number.");
}
}
}
}
});
}
//Class responsible for displaying the grid containing the Rectangles
public class GridDisplay {
private GridPane gridPane;
private int nbRow;
private int nbColumn;
private int width;
private int height;
private double hGap;
private double vGap;
public GridDisplay(int width, int height) {
this.gridPane = new GridPane();
this.width = width;
this.height = height;
build();
}
private void build() {
this.hGap = 0.1 * width;
this.vGap = 0.1 * height;
gridPane.setVgap(vGap);
gridPane.setHgap(hGap);
gridPane.setPrefSize(width, height);
initializeDisplay(width, height);
}
//Builds the first display (correctly) : adds a Rectangle for the number
//of rows and columns
private void initializeDisplay(int width, int height) {
nbRow = height / 100;
nbColumn = width / 100;
for (int i = 0; i < nbColumn; i++) {
for (int j = 0; j < nbRow; j++) {
Rectangle rectangle = new Rectangle(100, 100);
rectangle.setStroke(Paint.valueOf("orange"));
rectangle.setFill(Paint.valueOf("steelblue"));
gridPane.add(rectangle, i, j);
}
}
}
//Function detailed in post
//Called in updateDisplay()
public void refreshConstraints() {
gridPane.getRowConstraints().clear();
gridPane.getColumnConstraints().clear();
for (int i = 0; i < nbRow; i++) {
RowConstraints rConstraint = new RowConstraints();
rConstraint.setPercentHeight(100 / nbRow - ((nbRow - 1) * 10 / nbRow));
gridPane.getRowConstraints().add(rConstraint);
}
for (int i = 0; i < nbColumn; i++) {
ColumnConstraints cConstraint = new ColumnConstraints();
cConstraint.setPercentWidth(100 / nbColumn - ((nbColumn - 1) * 10 / nbColumn));
gridPane.getColumnConstraints().add(cConstraint);
}
}
public void setColumns(int newColumns) {
nbColumn = newColumns;
}
public void setRows(int newRows) {
nbRow = newRows;
}
public GridPane getDisplay() {
return gridPane;
}
//Function called when refreshing the display
public void updateDisplay() {
Platform.runLater(new Runnable() {
@Override
public void run() {
//The gridpane is cleared of the previous children
gridPane.getChildren().clear();
//A new rectangle is added for row*column
for (int i = 0; i < nbColumn; i++) {
for (int j = 0; j < nbRow; j++) {
Rectangle rectangle = new Rectangle(100, 100);
rectangle.setStroke(Paint.valueOf("orange"));
rectangle.setFill(Paint.valueOf("steelblue"));
gridPane.add(rectangle, i, j);
}
}
//Call to this function to update the grid's constraints
refreshConstraints();
}
});
}
}
}