JavaFX GridPane 单元格不会根据内容自动调整大小
JavaFX GridPane cells not auto-sizing to content
我正在编写一个生成随机颜色网格的 JavaFX 程序。调整 window 大小时,网格应调整为尽可能大,同时仍保持正方形并在文本底部留下 space。
一切正常,但我遇到的问题是调整大小时 GridPane 中留下的间隙很小。随着 window 调整大小时,间隙的大小会略有变化。有人可以帮我弄清楚如何消除这些差距吗?我包括完整的代码。不会太长。谢谢。
import java.util.Random;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
* Uses a 2D array to populate a grid of squares with random colors.
*/
public class Lab7 extends Application {
private Color[][] colorGrid = new Color[8][8];
private int redCellCount = 0;
/**
* Tells the program to start with the start() method since it is a JavaFX
* Application
*
* @param args
* arguments supplied to the program
*/
public static void main(String[] args) {
Application.launch(args);
}
/**
* Constructor for the class instantiates the 2D array of Color objects with
* random colors.
*/
public Lab7() {
// array of 12 awt colors not including white since it is used as the
// background color
Color[] colorList = { Color.BLACK, Color.BLUE, Color.CYAN, Color.DARKGREY, Color.GRAY, Color.GREEN,
Color.LIGHTGRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.YELLOW };
// populate the 2D array of colors with random colors from the colorList
for (int i = 0; i < colorGrid.length; i++) {
for (int j = 0; j < colorGrid[i].length; j++) {
Random rand = new Random();
int colorCode = rand.nextInt(12);
if (colorCode == 10) {
redCellCount++;
}
colorGrid[i][j] = colorList[colorCode];
}
}
}
/*
* overridden method of the Application class. This is the entry point of
* the JavaFX application
*/
@Override
public void start(Stage stage) throws Exception {
// a GridPane that will hold the checkerboard of colors
GridPane checkerboardPane = new GridPane();
// a root pane for the layout
BorderPane parentPane = new BorderPane();
// create the scene and set the root node as the BorderPane and have the
// initial size be 400x400 pixels and set the background color to white
Scene scene = new Scene(parentPane, 400, 400);
parentPane.setStyle("-fx-background-color: " + toRGBCode(Color.WHITE));
// a Text object to display the number of red squares
Text redCellCountText = new Text("There are " + redCellCount + " red squares.");
// put the colorGrid in the center of the GridPane and the
// redCellCountText to the bottom
parentPane.setCenter(checkerboardPane);
parentPane.setBottom(redCellCountText);
// create 64 rectangles, fill them with the colors in the colorGrid and
// set a mouse click event handler
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
Rectangle rect = new Rectangle(scene.getWidth() / 8, scene.getWidth() / 8);
// bind the width property of the rectangle to 1/8 of the
// smaller of the scene width or height, leave 50 pixels at the
// bottom for the Text
rect.widthProperty()
.bind(Bindings
.when(scene.widthProperty().lessThanOrEqualTo(scene.heightProperty().subtract(50)))
.then(scene.widthProperty().divide(8))
.otherwise(scene.heightProperty().subtract(50).divide(8)));
// bind the width of the rectangle to its height so it will
// always be square
rect.heightProperty().bind(rect.widthProperty());
// set the color of the rectangle to correspond to the colorGrid
rect.setStyle("-fx-fill: " + toRGBCode(colorGrid[i][j]));
// set an event listener for the rectangle to handle when the
// user clicks on it
rect.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// if the rectangle is not red
if (!((Rectangle) event.getSource()).getFill().equals(Color.RED)) {
// set its color to red
((Rectangle) event.getSource()).setFill(Color.RED);
// increment the redCellCount and update the text
redCellCount++;
redCellCountText.setText("There are " + redCellCount + " red squares.");
}
}
});
// add the rectangle to its respective square in the GridPane
checkerboardPane.add(rect, j, i);
}
}
// set the scene in the stage and set its title
stage.setScene(scene);
stage.setTitle("Lab7");
// show the stage to make it visible
stage.show();
}
/**
*
* @param color
* The JavaFX Color to convert
* @return the rgb code representing the JavaFX Color
*/
public static String toRGBCode(Color color) {
return String.format("#%02X%02X%02X", (int) (color.getRed() * 255), (int) (color.getGreen() * 255),
(int) (color.getBlue() * 255));
}
}
嗯,这些小差距来自于计算所有 Rectangles
的宽度,当您将值绑定到 Scene
的高度时。
肮脏的解决方案是使用 GridPane.setMargin(Node child, Insets value):
在矩形周围添加 -1 边距
// add the rectangle to its respective square in the GridPane
checkerboardPane.add(rect, j, i);
GridPane.setMargin(rect, new Insets(-1, -1, -1, -1));
GridPane
在布局过程中使用节点的宽度。由于除以 8 的结果可能不是整数。默认情况下 GridPane
将其子项的边界坐标四舍五入为整数值,这会导致您观察到的间隙。
将snapToPixel
设置为false
可以获得更好的结果:
checkerboardPane.setSnapToPixel(false);
虽然结果不会完美。如果您完全想消除间隙,请自行将大小四舍五入为整数值。此外,为了效率起见,只需为大小创建一个 NumberBinding
。另外Shape
的Bindings.min
can help you get simpler code for the size. Also the fill
可以直接设置为Color
:
NumberBinding size = Bindings.min(scene.widthProperty(),
scene.heightProperty().subtract(50))
.divide(8);
// binding for size rounded down
NumberBinding roundedSize = Bindings.createIntegerBinding(() -> size.intValue(), size);
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
Rectangle rect = new Rectangle();
rect.widthProperty().bind(roundedSize);
rect.heightProperty().bind(roundedSize);
rect.setFill(colorGrid[i][j]);
// click handler
checkerboardPane.add(rect, j, i);
}
}
我正在编写一个生成随机颜色网格的 JavaFX 程序。调整 window 大小时,网格应调整为尽可能大,同时仍保持正方形并在文本底部留下 space。
一切正常,但我遇到的问题是调整大小时 GridPane 中留下的间隙很小。随着 window 调整大小时,间隙的大小会略有变化。有人可以帮我弄清楚如何消除这些差距吗?我包括完整的代码。不会太长。谢谢。
import java.util.Random;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
* Uses a 2D array to populate a grid of squares with random colors.
*/
public class Lab7 extends Application {
private Color[][] colorGrid = new Color[8][8];
private int redCellCount = 0;
/**
* Tells the program to start with the start() method since it is a JavaFX
* Application
*
* @param args
* arguments supplied to the program
*/
public static void main(String[] args) {
Application.launch(args);
}
/**
* Constructor for the class instantiates the 2D array of Color objects with
* random colors.
*/
public Lab7() {
// array of 12 awt colors not including white since it is used as the
// background color
Color[] colorList = { Color.BLACK, Color.BLUE, Color.CYAN, Color.DARKGREY, Color.GRAY, Color.GREEN,
Color.LIGHTGRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.YELLOW };
// populate the 2D array of colors with random colors from the colorList
for (int i = 0; i < colorGrid.length; i++) {
for (int j = 0; j < colorGrid[i].length; j++) {
Random rand = new Random();
int colorCode = rand.nextInt(12);
if (colorCode == 10) {
redCellCount++;
}
colorGrid[i][j] = colorList[colorCode];
}
}
}
/*
* overridden method of the Application class. This is the entry point of
* the JavaFX application
*/
@Override
public void start(Stage stage) throws Exception {
// a GridPane that will hold the checkerboard of colors
GridPane checkerboardPane = new GridPane();
// a root pane for the layout
BorderPane parentPane = new BorderPane();
// create the scene and set the root node as the BorderPane and have the
// initial size be 400x400 pixels and set the background color to white
Scene scene = new Scene(parentPane, 400, 400);
parentPane.setStyle("-fx-background-color: " + toRGBCode(Color.WHITE));
// a Text object to display the number of red squares
Text redCellCountText = new Text("There are " + redCellCount + " red squares.");
// put the colorGrid in the center of the GridPane and the
// redCellCountText to the bottom
parentPane.setCenter(checkerboardPane);
parentPane.setBottom(redCellCountText);
// create 64 rectangles, fill them with the colors in the colorGrid and
// set a mouse click event handler
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
Rectangle rect = new Rectangle(scene.getWidth() / 8, scene.getWidth() / 8);
// bind the width property of the rectangle to 1/8 of the
// smaller of the scene width or height, leave 50 pixels at the
// bottom for the Text
rect.widthProperty()
.bind(Bindings
.when(scene.widthProperty().lessThanOrEqualTo(scene.heightProperty().subtract(50)))
.then(scene.widthProperty().divide(8))
.otherwise(scene.heightProperty().subtract(50).divide(8)));
// bind the width of the rectangle to its height so it will
// always be square
rect.heightProperty().bind(rect.widthProperty());
// set the color of the rectangle to correspond to the colorGrid
rect.setStyle("-fx-fill: " + toRGBCode(colorGrid[i][j]));
// set an event listener for the rectangle to handle when the
// user clicks on it
rect.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// if the rectangle is not red
if (!((Rectangle) event.getSource()).getFill().equals(Color.RED)) {
// set its color to red
((Rectangle) event.getSource()).setFill(Color.RED);
// increment the redCellCount and update the text
redCellCount++;
redCellCountText.setText("There are " + redCellCount + " red squares.");
}
}
});
// add the rectangle to its respective square in the GridPane
checkerboardPane.add(rect, j, i);
}
}
// set the scene in the stage and set its title
stage.setScene(scene);
stage.setTitle("Lab7");
// show the stage to make it visible
stage.show();
}
/**
*
* @param color
* The JavaFX Color to convert
* @return the rgb code representing the JavaFX Color
*/
public static String toRGBCode(Color color) {
return String.format("#%02X%02X%02X", (int) (color.getRed() * 255), (int) (color.getGreen() * 255),
(int) (color.getBlue() * 255));
}
}
嗯,这些小差距来自于计算所有 Rectangles
的宽度,当您将值绑定到 Scene
的高度时。
肮脏的解决方案是使用 GridPane.setMargin(Node child, Insets value):
在矩形周围添加 -1 边距// add the rectangle to its respective square in the GridPane
checkerboardPane.add(rect, j, i);
GridPane.setMargin(rect, new Insets(-1, -1, -1, -1));
GridPane
在布局过程中使用节点的宽度。由于除以 8 的结果可能不是整数。默认情况下 GridPane
将其子项的边界坐标四舍五入为整数值,这会导致您观察到的间隙。
将snapToPixel
设置为false
可以获得更好的结果:
checkerboardPane.setSnapToPixel(false);
虽然结果不会完美。如果您完全想消除间隙,请自行将大小四舍五入为整数值。此外,为了效率起见,只需为大小创建一个 NumberBinding
。另外Shape
的Bindings.min
can help you get simpler code for the size. Also the fill
可以直接设置为Color
:
NumberBinding size = Bindings.min(scene.widthProperty(),
scene.heightProperty().subtract(50))
.divide(8);
// binding for size rounded down
NumberBinding roundedSize = Bindings.createIntegerBinding(() -> size.intValue(), size);
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
Rectangle rect = new Rectangle();
rect.widthProperty().bind(roundedSize);
rect.heightProperty().bind(roundedSize);
rect.setFill(colorGrid[i][j]);
// click handler
checkerboardPane.add(rect, j, i);
}
}