JavaFX网格渲染问题:如何让线条显示在矩形Tile上层?
JavaFX网格线条被相邻Tile矩形遮挡的解决方案
问题说明
我是JavaFX新手,制作GridTile16网格时遇到线条被相邻Tile矩形遮挡的问题。希望所有线条显示在所有矩形上层,尝试过.toFront()、.toBack()和viewOrderProperty(),但这些方法仅对当前Tile的子节点生效,无法解决跨Tile的层级问题。
相关代码
创建网格的代码
private static void makeGrid(Group root, int gridSize, ArrayList<String> grid, GridTile16[][] gridTiles, Scene scene) { for (int i = 0; i < grid.size(); i++) { for (int j = 0; j < grid.get(i).length(); j++) { GridTile16 newGridTile = new GridTile16(i * gridSize, j * gridSize, gridSize, grid.get(i).charAt(j)); root.getChildren().add(newGridTile); gridTiles[i][j] = newGridTile; } } }
GridTile基类代码
import javafx.scene.Group; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; public class GridTile extends Group { protected int x; protected int y; protected int size; protected Rectangle rectangle; public GridTile(int x, int y, int size) { this.x = x; this.y = y; this.size = size; Rectangle rectangle = new Rectangle(x, y, size, size); rectangle.setFill(Color.BLACK); rectangle.setStroke(Color.WHITE); this.getChildren().add(rectangle); this.rectangle = rectangle; } public void resetColor() { this.rectangle.setFill(Color.BLACK); } public int getX() { return y/size; } public int getY() { return x/size; } }
GridTile16子类代码
import com.programs.GridTile; import javafx.scene.paint.Color; import javafx.scene.shape.Line; public class GridTile16 extends GridTile { private boolean[] isFromLeftRightTopBot = new boolean[4]; public GridTile16(int y, int x, int size, char c) { super(x, y , size); if (c != '.') { Line line = null; switch (c) { case '-': line = new Line(x, y + size / 2, x + size, y + size / 2); break; case '|': line = new Line(x + size / 2, y, x + size / 2, y + size); break; case '/': line = new Line(x, y + size, x + size, y); break; case '\\': line = new Line(x, y, x + size, y + size); break; } line.setStroke(Color.WHITE); line.setStrokeWidth(5); this.getChildren().add(line); } } public boolean getIsFromDirection(int direction) { return this.isFromLeftRightTopBot[direction]; } public void setIsFromDirection(int direction) { this.isFromLeftRightTopBot[direction] = true; this.rectangle.setFill(Color.BLUE); Line newLine = null; if (direction == 0) { newLine = new Line(this.x, this.y + this.size / 2, this.x + this.size / 2, this.y + this.size / 2); } else if (direction == 1) { newLine = new Line(this.x + this.size, this.y + this.size / 2, this.x + this.size / 2, this.y + this.size / 2); } else if (direction == 2) { newLine = new Line(this.x + this.size / 2, this.y, this.x + this.size / 2, this.y + this.size / 2); } else if (direction == 3) { newLine = new Line(this.x + this.size / 2, this.y + this.size, this.x + this.size / 2, this.y + this.size / 2); } newLine.setStrokeWidth(2); newLine.setStroke(Color.RED); this.getChildren().add(newLine); } }
主程序代码
public class App extends Application { private static final int gridSize = 40; private static double zoomFactor = 1; public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws FileNotFoundException { Group root = new Group(); Scene scene = new Scene(root, 400, 400, Color.BLACK); stage.setFullScreen(true); stage.setFullScreenExitHint(""); stage.setTitle("Visualisation"); stage.setScene(scene); stage.show(); scene.widthProperty().addListener((obs, oldVal, newVal) -> { resetSize(scene, root); }); scene.heightProperty().addListener((obs, oldVal, newVal) -> { resetSize(scene, root); }); File file = new File(<INPUT>); Scanner scanner = new Scanner(file); ArrayList<String> grid = new ArrayList<>(); while (scanner.hasNextLine()) { grid.add(scanner.nextLine()); } scanner.close(); GridTile16[][] gridTiles = new GridTile16[grid.size()][grid.get(0).length()]; makeGrid(root, gridSize, grid, gridTiles, scene); resetSize(scene, root); } private static void resetSize(Scene scene, Group root) { root.setLayoutX((scene.getWidth() - root.prefWidth(-1)) / 2); root.setLayoutY((scene.getHeight() - root.prefHeight(-1)) / 2); double scaleX = scene.getWidth() / root.getBoundsInLocal().getWidth(); double scaleY = scene.getHeight() / root.getBoundsInLocal().getHeight(); zoomFactor = Math.min(scaleX, scaleY) - 0.01; updateTransforms(root); } private static void updateTransforms(Group root) { root.setScaleX(zoomFactor); root.setScaleY(zoomFactor); } }
程序输入
.|...\.... |.-.\..... .....|-... ........|. .......... .........\ ..../.\\.. .-.-/..|.. .|....-|.\ ..//.|....
解决方案
核心思路是分离矩形层和线条层,让所有线条统一放在顶层容器中,从根本上避免被后续添加的Tile矩形遮挡。
具体实现步骤
- 在主程序中拆分root容器,新增两个独立Group分别存放矩形和线条:
// 替换原有root初始化代码 Group rectangleLayer = new Group(); Group lineLayer = new Group(); Group root = new Group(rectangleLayer, lineLayer);
JavaFX容器内子节点按添加顺序渲染,后添加的lineLayer会自动在rectangleLayer上方显示。
- 修改
GridTile16类,添加线条层参数并调整线条添加逻辑:
public class GridTile16 extends GridTile { private boolean[] isFromLeftRightTopBot = new boolean[4]; private Group lineLayer; // 新增线条层成员变量 // 修改构造函数,传入lineLayer public GridTile16(int y, int x, int size, char c, Group lineLayer) { super(x, y , size); this.lineLayer = lineLayer; if (c != '.') { Line line = null; switch (c) { case '-': line = new Line(x, y + size / 2, x + size, y + size / 2); break; case '|': line = new Line(x + size / 2, y, x + size / 2, y + size); break; case '/': line = new Line(x, y + size, x + size, y); break; case '\\': line = new Line(x, y, x + size, y + size); break; } line.setStroke(Color.WHITE); line.setStrokeWidth(5); lineLayer.getChildren().add(line); // 将线条添加到顶层线条层 } } // 修改setIsFromDirection方法,同样将红色线条加入顶层线条层 public void setIsFromDirection(int direction) { this.isFromLeftRightTopBot[direction] = true; this.rectangle.setFill(Color.BLUE); Line newLine = null; if (direction == 0) { newLine = new Line(this.x, this.y + this.size / 2, this.x + this.size / 2, this.y + this.size / 2); } else if (direction == 1) { newLine = new Line(this.x + this.size, this.y + this.size / 2, this.x + this.size / 2, this.y + this.size / 2); } else if (direction == 2) { newLine = new Line(this.x + this.size / 2, this.y, this.x + this.size / 2, this.y + this.size / 2); } else if (direction == 3) { newLine = new Line(this.x + this.size / 2, this.y + this.size, this.x + this.size / 2, this.y + this.size / 2); } newLine.setStrokeWidth(2); newLine.setStroke(Color.RED); lineLayer.getChildren().add(newLine); // 调整线条添加目标 } // 保留原有getIsFromDirection方法 public boolean getIsFromDirection(int direction) { return this.isFromLeftRightTopBot[direction]; } }
- 更新
makeGrid方法,传入拆分后的两个容器:
private static void makeGrid(Group rectangleLayer, Group lineLayer, int gridSize, ArrayList<String> grid, GridTile16[][] gridTiles, Scene scene) { for (int i = 0; i < grid.size(); i++) { for (int j = 0; j < grid.get(i).length(); j++) { GridTile16 newGridTile = new GridTile16(i * gridSize, j * gridSize, gridSize, grid.get(i).charAt(j), lineLayer); rectangleLayer.getChildren().add(newGridTile); gridTiles[i][j] = newGridTile; } } }
- 在主程序中调用
makeGrid时传入对应容器:
makeGrid(rectangleLayer, lineLayer, gridSize, grid, gridTiles, scene);
方案优势
通过分层管理,一次性解决所有线条的层级问题,无需逐个调整节点顺序,代码逻辑更清晰,维护成本更低,完全符合JavaFX的渲染机制。
内容的提问来源于stack exchange,提问作者Dimnik
相关产品推荐
相关产品推荐

