有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何按网格中的位置从GridPane中删除节点(圆形和按钮对象)?

我在这个问题上被困了很长一段时间,我们正在处理一个FXML文件。我已经在这里看到并实现了这段代码:JavaFX: Get Node by row and column

public Node getNodeByRowColumnIndex(final int row,final int column,GridPane gridPane) {
        Node result = null;
        ObservableList<Node> childrens = gridPane.getChildren();
        for(Node node : childrens) {
            if(gridPane.getRowIndex(node) == row && gridPane.getColumnIndex(node) == column) {
                result = node;
                break;
            }
        }
        return result;
    }

这适用于我们在开始时在FXML文件中添加的所有节点,但对于我们稍后在程序(游戏)运行时手动添加的节点,它不起作用。然后它会得到一个空指针异常。如果我们打印出节点,我们会得到在运行时添加的节点:Group@181fe7,因此是一个内存地址。对于在运行前添加到fxml文件中的节点,我们确实会得到一个很好的字符串,其中包含有关节点的信息,因此在某种程度上,将节点添加到网格与在fxml中执行的方式不同。我们通过以下代码手动添加节点:

private void addSheep(Point point) {
    Circle sheep = new Circle(25);
    sheep.setFill(Color.BLUE);
    sheep.setOnMouseClicked(this::sheepSelected);
    pane.add(sheep, point.x, point.y);
    GridPane.setHalignment(animal, HPos.CENTER);
}

为什么会这么难?我只想找到gridpane中特定单元格上的内容,并删除其中一个对象。我花了大量的时间在一些看似微不足道的事情上


共 (1) 个答案

  1. # 1 楼答案

    在某个地方,您正在向GridPane添加一些节点,而没有指定columnIndexrowIndex。(在某些情况下,即使您自己没有显式添加它们,也可能发生这种情况。例如,您在GridPane上将gridLinesVisible设置为true。)看起来这些节点中至少有一个是^{};您描述的输出是来自Group实例的toString()的默认实现

    GridPane内部管理网格位置的方式是通过每个节点的property map设置特定值;此映射中列索引和行索引的值是Integer属性。getColumnIndex()getRowIndex()检索这些Integer属性并有效地调用它们的intValue(),因此如果没有设置它们,您将得到一个NullPointerException

    因此,一个快速解决方法可能是

    public Node getNodeByRowColumnIndex(final int row,final int column,GridPane gridPane) {
        Node result = null;
        ObservableList<Node> childrens = gridPane.getChildren();
        for(Node node : childrens) {
            if(node instanceof Circle && gridPane.getRowIndex(node) == row && gridPane.getColumnIndex(node) == column) {
                result = node;
                break;
            }
        }
        return result;
    }
    

    如果正确设置了列索引和行索引(并且您感兴趣)添加的所有节点都是Circles,那么这将起作用。显然,您可以添加一些其他逻辑来解决其他场景

    您在这里遇到困难的原因是您正在有效地尝试从视图组件(即GridPane)检索数据(某个位置的数据)。这是一种反模式:视图不应该存储应用程序数据,它们应该只是被动地观察(最多)。如果您在项目上还有大量的工作要做,您可能应该考虑对其进行重组,使其以数据为中心

    以下是一些代码片段,可以帮助您思考我在这里的意思:

    public class Sheep {
    
        // data for intrinsic state of sheep omitted..
    
        private final Circle view ;
    
        public Sheep() {
            this.view = new Circle(...);
            // configure view...
        }
    
        public Node getView() {
            return view ;
        }
    
        // logic etc... manipulates intrinsic state of sheep....
    }
    

    确保实现Point,以便在集合中正确使用它:

    public class Point {
        final int x, y ;
        @Override
        public boolean equals(Object other) {
            if (other.getClass() != Point.class) {
                return false ;
            }
            Point o = (Point)other ;
            return o.x == x && o.y == y ;
        }
        @Override
        public int hashCode() {
            return Objects.hash(x, y);
        }
    }
    

    游戏代码:

    ObservableMap<Point, Sheep> allSheep = FXCollections.observableHashMap();
    GridPane gameView = new GridPane();
    allSheep.addListener((Change<? extends Point, ? extends Sheep> c) -> {
        if (c.wasAdded()) {
            Point location = c.getKey();
            Sheep addedSheep = c.getValue();
            gameView.add(addedSheep.getView(), location.x, location.y);
        } else if (c.wasRemoved()) {
            Sheep removedSheep = c.getValue();
            gameView.getChildren().remove(removedSheep.getView());
        }
    }
    

    现在,视图已被有效地设置为自行处理,您只需处理数据,只需操作位置地图即可:

    public void addSheep(int x, int y) {
        allSheep.put(new Point(x,y), new Sheep());
    }
    
    public void removeSheep(int x, int y) {
        allSheep.remove(new Point(x, y));
    }
    

    请注意,地图包含与游戏相关的所有数据,因此您可以随时检查地图,并进行相应的更改。您应该能够将该逻辑从游戏的“视图”中分离出来,只需使用控制器/演示者类型的类封装它们之间的链接(例如,上面定义的地图侦听器,当地图中的条目发生更改时,它会更新网格)