我被困在这一段时间,我们正在使用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中特定单元格上的内容并删除其中一个对象。我花了很多时间做一些看似微不足道的事情。
答案 0 :(得分:1)
您正在某处向GridPane
添加一些节点,而未指定columnIndex
和rowIndex
。 (在某些情况下,即使您没有自己明确添加它们,也会发生这种情况。例如,您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));
}
请注意,地图包含与游戏相关的所有数据,因此您可以在需要时进行检查并进行适当的更改。您应该能够将该逻辑与游戏的“视图”分开,并且只需要控制器/演示者类型类封装它们之间的链接(例如,上面定义的地图侦听器,在地图中的条目时更新网格)变化)。