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

时间:2015-03-17 17:55:31

标签: java javafx fxml

我被困在这一段时间,我们正在使用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 个答案:

答案 0 :(得分:1)

您正在某处向GridPane添加一些节点,而未指定columnIndexrowIndex。 (在某些情况下,即使您没有自己明确添加它们,也会发生这种情况。例如,您gridLinesVisible上的true设置为GridPane。)它至少看起来像其中一个节点是Group;您描述的输出是来自toString()实例的Group的默认实现。

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;
}

如果您添加的列索引和行索引正确设置(并且您感兴趣)的所有节点都是Circle,则此方法将起作用。显然,您可以添加一些其他逻辑来解决其他情况。

您遇到困难的原因是您正在有效地尝试从视图组件(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));
}

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