访问 GridPane 节点的问题

Problems with accessing GridPane Node

我希望你能帮我做一个我想做的小项目。我正在尝试使用 JavaFX 为 GUI 创建一个视频游戏,但我遇到了一些麻烦。这是我为练习而制作的代码,我不知道为什么这不起作用。

public class Main extends Application {
public GridPane map;
private int MAXH = 40;
private int MAXV = 40;
Random r = new Random();

@Override
public void start(Stage primaryStage) throws Exception{

    map = new GridPane();
    map.setGridLinesVisible(true);

    map.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent mouseEvent) {
            //Get X position
            int posX = (int) mouseEvent.getX() / 40;
            //Get Y position
            int posY = (int) mouseEvent.getY() / 40;

            System.out.println("(" + posX + " " + posY + ")");

            Canvas c = getCanvasNode(map, posX, posY);
        }
    });

    for (int i = 0; i < MAXH; i++){
        for(int j = 0; j < MAXV; j++){
            //For each cell create a Canvas node
            Canvas c = new Canvas(40, 40);
            GraphicsContext gc = c.getGraphicsContext2D();
            int k = r.nextInt();


            //Randomly paint the Canvas
            if (k % 3 == 0) gc.setFill(Color.BLUE);
            else if (k % 2 == 0) gc.setFill(Color.RED);
            else if (k % 7 == 0)gc.setFill(Color.GREEN);

            //Filling the Canvas
            gc.fillRect(0, 0, 40, 40);
            //Adding to our Grid Pane
            map.add(c, i, j);
        }
    }

    primaryStage.setHeight(700);
    primaryStage.setWidth(1200);
    primaryStage.setScene(new Scene(map));
    primaryStage.show();


}

private Canvas getCanvasNode(GridPane gridPane, int col, int row) {
    for (Node node : gridPane.getChildren()) {
        if (GridPane.getColumnIndex(node) == col && GridPane.getRowIndex(node) == row) {
            return (Canvas) node;
        }
    }
    return null;
}

public static void main(String[] args) {
    launch(args);
}}

当我使用函数 "getCanvasNode" 时,问题就出现了,它产生了一个空指针异常,我不知道为什么。

我想做的是通过地图获取我点击的节点事件处理程序有人可以帮助我吗?

PD:我不知道这是否是最 efficient/simple 的方法,如果有人能给我一些提示,那就太棒了。我是 Java/JavaFX 的新人。

GridPane.getColumnIndex() returns Integer Java Object,它包装并用作原始 int [=62= 的装箱] 数据类型。原始数据类型永远不能是 null,但是 Object(因此 Integer)可以分配给 null。如果我们阅读 GridPane.getColumnIndex() 的 java api 文档,它说,

Returns:
the column index for the child or null if no column index was set

它可以 return 为空。声明

if (GridPane.getColumnIndex(node) == col) {}

隐式分解为

Integer colIndex = GridPane.getColumnIndex(node);
int colIndexVal = colIndex.intValue();
if (colIndexVal == col) {}

如果 colIndex 为空,则 colIndex.intValue() 抛出 NullPointerException。您的代码中发生了什么。

解决方案是考虑空值:

if (GridPane.getColumnIndex(node) != null 
     && GridPane.getColumnIndex(node) == col
     && GridPane.getRowIndex(node) != row
     && GridPane.getRowIndex(node) == row) {
            return (Canvas) node;
        }

另一种方法是将 col 和 row 的参数参数类型更改为 Integer

private Canvas getCanvasNode( GridPane gridPane, Integer col, Integer row )

并使用 equals() 因为现在我们正在比较两个 Integer 对象。这将处理空值:

if ( col.equals( GridPane.getColumnIndex( node ) ) 
     && row.equals( GridPane.getRowIndex( node ) ) ) {
    return ( Canvas ) node;
}

更多信息请继续阅读

  • Java 中的数据类型。装箱和拆箱
  • 比较 Java 原始类型和对象

关于程序本身,我认为您在那里使用了太多 Canvas 对象。您可以改用 JavaFX RectangleLabel 控件。

根据列值和行值访问子项可以简化为

Node node = gridPane.getChildren().get( col * MAXV + row + 1 );
return (node instanceof Canvas) ? ( Canvas ) node : null;

当你这样做时

for (Node node : gridPane.getChildren()) { ... }

您遍历网格窗格的所有子节点,无论这些节点是您自己添加的节点,还是属于网格窗格内部实现的一部分的节点。

你的情况实际发生的是,因为你已经调用了

map.setGridLinesVisible(true);

网格窗格添加了一个子节点来表示网格线。此节点没有为 columnIndexrowIndex 设置属性。所以当循环遇到那个节点时,GridPane.getColumnIndex(node) 抛出一个 NullPointerException。 (getColumnIndex 的实现在节点的 属性 映射中查找具有特定键的 Integer 对象,然后在结果上隐式调用 intValue()。所以如果没有值设置,它会尝试在 null 引用上调用 intValue()。)

所以你可以按照

的方式做一些事情来解决这个问题
Integer columnIndex = GridPane.getColumnIndex(node);

然后处理特殊情况:

if (columnIndex != null && columnIndex.intValue() == col)  

等等

但是,对于您要实现的目标,有一种更好的方法。与其在窗格中注册侦听器,然后深入了解单击了哪个子节点,不如在子节点上注册侦听器:

for (int i = 0; i < MAXH; i++){
    for(int j = 0; j < MAXV; j++){
        //For each cell create a Canvas node
        Canvas c = new Canvas(40, 40);
        GraphicsContext gc = c.getGraphicsContext2D();
        int k = r.nextInt();


        //Randomly paint the Canvas
        if (k % 3 == 0) gc.setFill(Color.BLUE);
        else if (k % 2 == 0) gc.setFill(Color.RED);
        else if (k % 7 == 0)gc.setFill(Color.GREEN);

        //Filling the Canvas
        gc.fillRect(0, 0, 40, 40);
        //Adding to our Grid Pane
        map.add(c, i, j);

        String message = "Click on cell ["+i+", "+j+"]";
        c.setOnMouseClicked(e -> {
            System.out.println(message);
            // do anything else you need with the canvas....
        });
    }
}