在JavaFX中将元素动态添加到固定大小的GridPane

我想在JavaFX中显示包含各种矩形的网格。 重要的是不能调整此网格的大小。

我选择了GridPane布局。 我动态地添加javafx.scene.shape.Rectangle 。 这是我的网格看起来像2行和4列。

网格4x2

resize后,我希望它保持相同的整体形状,也就是说每个Rectangle具有相同的大小并在我的Rectangle之间保持水平和垂直间隙。

但是,这是我用4×4网格得到的: 在此处输入图像描述

问题是:

  • 最后一行和最后一列的大小与Rectangle的其余部分的大小不同。
  • 差距已经消失。

这是我负责刷新显示的代码:

 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); } } 

使用setFillWidthsetHgrow没有结果,间隙保持在我的Rectangle之间,但是Rectangle s没有resize,它们与我的其余GUI元素重叠。


编辑 :MCVE代码:

 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() { @Override public void changed(ObservableValue 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() { @Override public void changed(ObservableValue 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(); } }); } } } 

看起来像TilePane比GridPane更适合这个用例。

2by43by2

 import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.TilePane; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; // java 8 code public class DynamicTiles extends Application { //Class containing grid (see below) private GridDisplay gridDisplay; //Class responsible for displaying the grid containing the Rectangles public class GridDisplay { private static final double ELEMENT_SIZE = 100; private static final double GAP = ELEMENT_SIZE / 10; private TilePane tilePane = new TilePane(); private Group display = new Group(tilePane); private int nRows; private int nCols; public GridDisplay(int nRows, int nCols) { tilePane.setStyle("-fx-background-color: rgba(255, 215, 0, 0.1);"); tilePane.setHgap(GAP); tilePane.setVgap(GAP); setColumns(nCols); setRows(nRows); } public void setColumns(int newColumns) { nCols = newColumns; tilePane.setPrefColumns(nCols); createElements(); } public void setRows(int newRows) { nRows = newRows; tilePane.setPrefRows(nRows); createElements(); } public Group getDisplay() { return display; } private void createElements() { tilePane.getChildren().clear(); for (int i = 0; i < nCols; i++) { for (int j = 0; j < nRows; j++) { tilePane.getChildren().add(createElement()); } } } private Rectangle createElement() { Rectangle rectangle = new Rectangle(ELEMENT_SIZE, ELEMENT_SIZE); rectangle.setStroke(Color.ORANGE); rectangle.setFill(Color.STEELBLUE); return rectangle; } } @Override public void start(Stage primaryStage) { //Represents the grid with Rectangles gridDisplay = new GridDisplay(2, 4); //Fields to specify number of rows/columns TextField rowField = new TextField("2"); TextField columnField = new TextField("4"); //Function to set an action when text field loses focus buildTextFieldActions(rowField, columnField); HBox fields = new HBox(10); fields.getChildren().add(rowField); fields.getChildren().add(new Label("x")); fields.getChildren().add(columnField); BorderPane mainPanel = new BorderPane(); mainPanel.setCenter(gridDisplay.getDisplay()); mainPanel.setTop(fields); Scene scene = new Scene(mainPanel, 1000, 800); 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((ov, t, t1) -> { if (!t1) { if (!rowField.getText().equals("")) { try { int nbRow = Integer.parseInt(rowField.getText()); gridDisplay.setRows(nbRow); } catch (NumberFormatException nfe) { System.out.println("Please enter a valid number."); } } } }); columnField.focusedProperty().addListener((ov, t, t1) -> { if (!t1) { if (!columnField.getText().equals("")) { try { int nbColumn = Integer.parseInt(columnField.getText()); gridDisplay.setColumns(nbColumn); } catch (NumberFormatException nfe) { System.out.println("Please enter a valid number."); } } } }); } } 

非常感谢您的回答。 TilePane确实更容易使用,虽然你写的并不能完全回答我的问题。

我希望有一个窗格, 孩子们可以在其中resize,而不是窗格本身。 似乎设置maxSizeprefSize没有任何影响。

编辑 :我设法在我的GridDisplay类中使用两个JavaFX Property ,对应于我的网格的固定高度和宽度:

 public class GridDisplay { private ReadOnlyDoubleProperty heightProperty; private ReadOnlyDoubleProperty widthProperty; ... } 

然后我为这些成员分配与构造函数中所需固定大小相对应的值。 网格内部子项的大小对应于网格高度和宽度的一小部分,具体取决于行数和列数。 这是我的updateDisplay()样子:

 public void updateDisplay() { gridPane.getChildren().clear(); for (int i = 0; i < nbColumn; i++) { for (int j = 0; j < nbRow; j++) { Rectangle rectangle = new Rectangle(100, 100); //Binding the fraction of the grid size to the width //and heightProperty of the child rectangle.widthProperty().bind(widthProperty.divide(nbColumn)); rectangle.heightProperty().bind(heightProperty.divide(nbRow)); gridPane.add(rectangle, i, j); } } } 
Interesting Posts