JavaFX GridPane单元格颜色设置 - 俄罗斯方块开发实践
2025-03-19 02:06:45
JavaFX GridPane 单元格颜色设置:俄罗斯方块开发实践
搞俄罗斯方块项目的时候,想用 GridPane 来划分游戏区域,把舞台(Stage)分割成一个个小格子。 初步设想是改变每个单元格的颜色来模拟方块,但不知道怎么下手。 试过直接加彩色矩形,不行。颜色信息是放在一个15x10的二维数组 colorArray
里面的,每个索引对应一个颜色。
public void start(Stage primaryStage) throws Exception {
pane = new GridPane();
pane.setGridLinesVisible(true);
pane.setVgap(40);
pane.setHgap(40);
primaryStage.setResizable(false);
scene = new Scene(pane,height,width);
for(int x = 0; x < 16; x++){
pane.getRowConstraints().addAll(getFifteenRowConstraints());
for(int y = 0; y<10; y++){
pane.getColumnConstraints().addAll(getTenColumnConstraints());
}
}
primaryStage.setScene(scene);
primaryStage.setTitle("Russian Game");
primaryStage.show();
}
对行约束(Row Constraints)和列约束(Column Constraints)具体干啥的,还真不太清楚,就知道现在格子是均匀分隔开了,有线隔着。
现在的问题就是:怎么单独改变 GridPane 里每个单元格的颜色?
问题原因分析
直接添加彩色矩形到 GridPane 不起作用,是因为 GridPane 默认情况下不会直接把子节点作为背景。 GridPane 的布局方式是管理子节点的位置和大小,而不是把它们“画”在自己身上。设置了 setGridLinesVisible(true)
,能看到网格线,但网格线和单元格背景是两码事。
至于行约束和列约束,它们决定了 GridPane 的行高和列宽。 getFifteenRowConstraints()
和 getTenColumnConstraints()
(代码中未给出实现)很可能是用来设置行高和列宽的具体数值。 但是这些约束并不能直接控制单元格颜色。
解决方案
要实现改变 GridPane 单元格颜色,有几种方法。
1. 使用背景填充 (Background Fill)
最直接的方法是给每个单元格添加一个 Pane
或 Region
,然后设置它们的背景颜色。
原理:
Pane
和 Region
都是可以设置背景的节点。把它们作为 GridPane 的子节点,并设置好对应的行和列,就能通过控制这些节点的背景色来改变单元格的颜色。
代码示例:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class TetrisGrid extends Application {
private GridPane pane;
private final int rows = 15;
private final int cols = 10;
private Color[][] colorArray = new Color[rows][cols]; //假设已初始化
private Pane[][] cellPanes = new Pane[rows][cols]; // 保存每个单元格的Pane
@Override
public void start(Stage primaryStage) {
//初始化colorArray,此处随便设置几个
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
colorArray[i][j] = Color.WHITE; // 默认白色
}
}
colorArray[0][0] = Color.RED;
colorArray[1][1] = Color.BLUE;
pane = new GridPane();
pane.setGridLinesVisible(true);
// 这里可以根据需要调整间距,或者直接去掉
// pane.setVgap(1); //垂直
// pane.setHgap(1); //水平
primaryStage.setResizable(false);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
Pane cellPane = new Pane();
cellPane.setPrefSize(40, 40); // 设置单元格大小
updateCellColor(i, j); // 初始化时也更新颜色
pane.add(cellPane, j, i); // 注意:GridPane.add(node, columnIndex, rowIndex)
cellPanes[i][j] = cellPane; // 保存Pane引用
}
}
Scene scene = new Scene(pane, 400, 600); // 根据单元格大小调整场景大小
primaryStage.setScene(scene);
primaryStage.setTitle("Tetris Grid");
primaryStage.show();
}
//更新单个格子颜色的方法.
public void updateCellColor(int row, int col) {
Pane cellPane = cellPanes[row][col]; // 获取对应的Pane
cellPane.setBackground(new javafx.scene.layout.Background(new javafx.scene.layout.BackgroundFill(colorArray[row][col], null, null)));
}
public static void main(String[] args) {
launch(args);
}
}
解释:
- 创建
Pane
对象作为每个单元格。 setPrefSize()
设置单元格的 preferred 大小。updateCellColor()
用于设置对应Pane
的背景色,使用了colorArray
中的颜色。pane.add(cellPane, j, i);
将Pane
添加到 GridPane 的指定位置。注意add
方法的参数顺序:先列索引,后行索引。- 使用了一个
cellPanes
数组保存单元格中每一个Pane
.
2. 使用矩形 (Rectangle) 并设置填充色
虽然一开始你说直接添加矩形不行,但可能是方法不对。Rectangle
本身可以设置填充色,正确使用也能实现目标。
原理:
Rectangle
是一个形状节点,可以设置填充颜色 (setFill()
)。把它添加到 GridPane 作为子节点,就能通过改变填充色来改变单元格的外观。
代码示例:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.scene.layout.StackPane;
public class TetrisGridRect extends Application {
private GridPane pane;
private final int rows = 15;
private final int cols = 10;
private Color[][] colorArray = new Color[rows][cols]; // 假设已初始化
private Rectangle[][] cellRects = new Rectangle[rows][cols];
@Override
public void start(Stage primaryStage) {
//初始化colorArray,此处随便设置几个
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
colorArray[i][j] = Color.WHITE; // 默认白色
}
}
colorArray[0][0] = Color.RED;
colorArray[1][1] = Color.BLUE;
pane = new GridPane();
pane.setGridLinesVisible(true);
primaryStage.setResizable(false);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
Rectangle rect = new Rectangle(40, 40); // 设置矩形大小
updateCellColor(i,j);
pane.add(rect, j, i);
cellRects[i][j] = rect;
}
}
Scene scene = new Scene(pane, 400, 600);
primaryStage.setScene(scene);
primaryStage.setTitle("Tetris Grid with Rectangles");
primaryStage.show();
}
public void updateCellColor(int row, int col) {
cellRects[row][col].setFill(colorArray[row][col]);
}
public static void main(String[] args) {
launch(args);
}
}
解释:
类似第一种, 只是将Pane替换成Rectangle
- 创建
Rectangle
对象,并设置其大小。 setFill()
设置矩形的填充颜色。pane.add(rect, j, i);
将Rectangle
添加到 GridPane。- 使用了一个
cellRects
数组保存单元格中每一个Rectangle
.
3. 使用 Canvas (进阶)
如果对性能要求比较高,或者需要更精细的绘图控制,可以使用 Canvas
。
原理:
Canvas
提供了一个绘图区域,可以在上面直接绘制像素。可以通过计算每个单元格的坐标,然后用指定的颜色填充对应的矩形区域。
代码示例:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class TetrisGridCanvas extends Application {
private final int rows = 15;
private final int cols = 10;
private final int cellSize = 40; // 每个单元格的大小
private Color[][] colorArray = new Color[rows][cols];
private Canvas canvas;
@Override
public void start(Stage primaryStage) {
//初始化colorArray,此处随便设置几个
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
colorArray[i][j] = Color.WHITE; // 默认白色
}
}
colorArray[0][0] = Color.RED;
colorArray[1][1] = Color.BLUE;
canvas = new Canvas(cols * cellSize, rows * cellSize);
Pane root = new Pane(canvas); // 使用 Pane 作为根节点
drawGrid(); // 初始绘制
Scene scene = new Scene(root, cols * cellSize, rows * cellSize);
primaryStage.setScene(scene);
primaryStage.setTitle("Tetris Grid with Canvas");
primaryStage.show();
}
public void updateCellColor(int row, int col, Color color) {
colorArray[row][col] = color;
drawCell(row, col);
}
private void drawGrid() {
GraphicsContext gc = canvas.getGraphicsContext2D();
// 绘制网格线
gc.setStroke(Color.LIGHTGRAY);
for (int i = 0; i <= rows; i++) {
gc.strokeLine(0, i * cellSize, cols * cellSize, i * cellSize);
}
for (int j = 0; j <= cols; j++) {
gc.strokeLine(j * cellSize, 0, j * cellSize, rows * cellSize);
}
// 根据colorArray填充颜色
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
drawCell(i,j);
}
}
}
private void drawCell(int row, int col){
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(colorArray[row][col]);
gc.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
}
public static void main(String[] args) {
launch(args);
}
}
解释:
Canvas
的大小设置为整个游戏区域的大小。drawGrid()
绘制网格线和初始的单元格颜色。drawCell()
绘制单个方格- 通过
updateCellColor
来更新colorArray
,并调用drawCell()
重绘指定单元格
这种方法的优势: 对于大量单元格, 使用Canvas手动绘制, 性能优于创建大量的Pane或Rectangle对象.
安全建议
上述方法都主要集中在 UI 层面。 在实际的俄罗斯方块游戏中,颜色数组 (colorArray
) 通常对应着游戏逻辑中的一个数据结构(例如一个二维数组表示的棋盘)。
- 数据一致性: 确保 UI 上的颜色变化与游戏逻辑中的数据保持同步。 UI 只是数据的可视化表示,不要直接在 UI 上修改游戏数据。
- 边界检查: 在访问
colorArray
时,注意进行边界检查,防止数组越界。 - 模块化: 把 UI 渲染和游戏逻辑分开, 方便代码维护和测试。 可以考虑 MVC (Model-View-Controller) 或类似的设计模式。
- 代码清晰 : 及时更新和重新绘制格子颜色,逻辑应该清晰
以上就是给 GridPane 单元格设置颜色的几种办法。做俄罗斯方块,选哪个都行,看具体需求和个人喜好。希望这些信息有用!