在 JavaFX 中将元素动态添加到固定大小的 GridPane
Posted
技术标签:
【中文标题】在 JavaFX 中将元素动态添加到固定大小的 GridPane【英文标题】:Dynamically add elements to a fixed-size GridPane in JavaFX 【发布时间】:2014-06-09 23:25:20 【问题描述】:我想在 JavaFX 中显示一个包含不同数量矩形的网格。不能调整此网格的大小,这一点很重要。
我选择了GridPane
布局。我动态添加javafx.scene.shape.Rectangle
到它。这是 2 行 4 列的网格的样子。
在调整大小时,我希望它保持相同的整体形状,也就是说每个Rectangle
具有相同的大小并在我的Rectangle
s 之间保持水平和垂直间隙。
但是,这是我使用 4x4 网格得到的结果:
问题是:
最后一行和最后一列的大小与Rectangle
s 的其余部分不同。
差距已经消失。
这是我负责刷新显示的代码:
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);
使用setFillWidth
和setHgrow
也不会产生任何结果,我的Rectangle
s 之间的间隙保持不变,但Rectangle
s 没有调整大小,它们与我的其余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<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();
);
【问题讨论】:
将 GridPane 粘贴到一个组中。它解决了你的问题吗?如果没有,请编辑您的帖子以包含MCVE。 使用组不会改变任何事情。包括 MCVE。要更改行数,请单击第一个文本字段,输入一个整数,然后单击另一个文本字段。这里的问题似乎有点不同,当我不希望 GridPane 调整大小时。 【参考方案1】:似乎TilePane 比 GridPane 更适合此用例。
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.");
);
【讨论】:
【参考方案2】:非常感谢您的回答。 TilePane
s 确实更容易使用,尽管您所写的内容并不能完全回答我的问题。
我想要一个窗格,children 可以在其中调整大小,而不是窗格本身。似乎设置maxSize
和prefSize
没有任何效果。
编辑:我设法在我的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);
【讨论】:
以上是关于在 JavaFX 中将元素动态添加到固定大小的 GridPane的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Java 11 中将 JavaFX 运行时添加到 Eclipse?