JavaFX GridPane设置列/行后节点边界不正确

JavaFX GridPane设置列/行后节点边界不正确,java,javafx,Java,Javafx,当设置GridPane中包含的节点的新行和列位置时,边界直到稍后才会更新(我猜JavaFX会在循环结束时计算这些边界)。我想知道是否有办法强制重新计算这些边界,以便boundsAfter包含正确的值,而不是与boundsPre相同的值(如输出所示) 输出: PRE-MOVE: BoundingBox [minX:25.0, minY:339.0, minZ:0.0, width:243.0, height:116.0, depth:0.0, maxX:268.0, maxY:455.0, max

当设置GridPane中包含的节点的新行和列位置时,边界直到稍后才会更新(我猜JavaFX会在循环结束时计算这些边界)。我想知道是否有办法强制重新计算这些边界,以便
boundsAfter
包含正确的值,而不是与
boundsPre
相同的值(如输出所示)

输出:

PRE-MOVE: BoundingBox [minX:25.0, minY:339.0, minZ:0.0, width:243.0, height:116.0, depth:0.0, maxX:268.0, maxY:455.0, maxZ:0.0]
BOUNDS: BoundingBox [minX:25.0, minY:339.0, minZ:0.0, width:243.0, height:116.0, depth:0.0, maxX:268.0, maxY:455.0, maxZ:0.0]
PRE-MOVE: BoundingBox [minX:25.0, minY:87.0, minZ:0.0, width:243.0, height:116.0, depth:0.0, maxX:268.0, maxY:203.0, maxZ:0.0]
BOUNDS: BoundingBox [minX:25.0, minY:213.0, minZ:0.0, width:243.0, height:116.0, depth:0.0, maxX:268.0, maxY:329.0, maxZ:0.0]

解决方案1

设置行或列位置后,在基本窗格上调用
layout()
方法。根据Oracle JavaFX文档,这将“在该父节点下的场景图上执行自顶向下的布局过程”,这将导致重新计算属于该窗格的所有子节点的边界

Bounds boundsPre = theNode.localToScene(theNode.getBoundsInLocal());
System.out.println("PRE-MOVE: " + boundsPre);

GridPane.setColumnIndex(theNode, newCol);
GridPane.setRowIndex(theNode, newRow);

baseLayout.layout(); // Causes a layout pass which updates the bounds

Bounds boundsAfter = theNode.localToScene(theNode.getBoundsInLocal());
System.out.println("BOUNDS: " + boundsAfter );
输出:

PRE-MOVE: BoundingBox [minX:25.0, minY:339.0, minZ:0.0, width:243.0, height:116.0, depth:0.0, maxX:268.0, maxY:455.0, maxZ:0.0]
BOUNDS: BoundingBox [minX:25.0, minY:339.0, minZ:0.0, width:243.0, height:116.0, depth:0.0, maxX:268.0, maxY:455.0, maxZ:0.0]
PRE-MOVE: BoundingBox [minX:25.0, minY:87.0, minZ:0.0, width:243.0, height:116.0, depth:0.0, maxX:268.0, maxY:203.0, maxZ:0.0]
BOUNDS: BoundingBox [minX:25.0, minY:213.0, minZ:0.0, width:243.0, height:116.0, depth:0.0, maxX:268.0, maxY:329.0, maxZ:0.0]
解决方案2

如果不想立即更新边界,请遵守
boundsInLocal
属性。JavaFX完成下一个布局过程时将触发此事件

theNode.boundsInLocalProperty().addListener((observableValue, bounds, updatedBounds) ->
{
    System.out.println("BOUNDS: " + updatedBounds);
});
信用

正如Slaw在问题评论中指出的:

因此,要么观察boundsInLocal属性并在其更改时作出反应,要么在场景根上调用applyCss(),后跟layout()。第一种方法可能是“更清洁”


请…在我之前的评论中犯了一个错误。虽然更改行/列索引不应影响
boundsInLocal
,除非更改这些索引会导致大小更改,否则我没有意识到您正在将边界从本地转换到场景。在这种情况下,更改行/列索引应该会给出不同的界限。但是,边界通常仅在布局过程中计算。因此,要么观察
boundsInLocal
属性并在其更改时作出反应,要么在场景根上调用
applyCss()
,然后调用
layout()
。第一种方法可能是“更干净”。@Slaw My软件允许用户将节点从一个网格窗格单元格拖到另一个网格窗格单元格。为了实现这一点,它在MouseEvent(拖动事件)期间使用节点的坐标。不幸的是,在布局过程发生之前,此拖动事件可能会触发多次,从而导致在移动代码中使用不正确的坐标,从而导致节点移动不正确。我将尝试停止拖动事件的进一步处理,直到观察到
boundsInLocal
更改,以便只使用正在移动的节点的正确坐标。@Slaw我没有及时看到您的编辑,感谢您进一步更新它<代码>applyCss()
布局()
代替我问题中的注释
//Todo:Force JavaFX在这里重新计算边界
非常有效。你愿意提出这个问题的解决方案吗?观察
boundsInLocal
属性更改也可能是其他不想立即更新边界的人的解决方案。谢谢你的时间和帮助!这感觉有点像你完全使用了错误的方法。当我玩棋盘游戏之类的应用程序时,我使用的方法是在每个网格单元中放置一个窗格。我将表示片段的节点添加到相应的窗格中,该窗格对应于您希望它位于的单元格。然后,只需将拖动处理程序注册到更新
translateX
translateY
的片段中,并将拖放处理程序注册到将拖动片段移动到新窗格的窗格中。这样就避免了完全被底层布局的细节弄得脏兮兮的。