我有一个包含一些VBox
和Line
s的Pane
。我想将 Line
s 的端点绑定到 VBox
s 内部的"锚点"(基本上我想绑定到VBox
中任意嵌套的Node
的位置(,但我无法弄清楚什么值代表Node
相对于顶部的位置Pane
。我尝试过布局属性、翻译属性以及局部边界和父边界,但它们似乎都不起作用。我错过了什么?
(如果需要,我可以提供一个代码示例,但我认为它无助于更好地解释我的问题,因为我无法让它工作。
编辑:我忘了提到VBox
可以在窗格中自由移动,这就是为什么我需要绑定线条的原因。
编辑:这里有一些来源显示我的进步。我可以得到正确的位置,但它没有约束力
public class Graph extends Application {
private double startX;
private double startY;
private ObjectBinding<Bounds> bounds;
private DoubleBinding tx;
private DoubleBinding ty;
@Override
public void start(Stage primaryStage) throws Exception {
Pane pane = new Pane();
Circle target = new Circle(5, Color.RED);
VBox node = wrap(target);
Line connector = new Line();
bounds = Bindings.createObjectBinding(() -> {
Bounds nodeLocal = target.getBoundsInLocal();
Bounds nodeScene = target.localToScene(nodeLocal);
Bounds nodePane = pane.sceneToLocal(nodeScene);
return nodePane;
},
target.boundsInLocalProperty(),
target.localToSceneTransformProperty(),
pane.localToSceneTransformProperty()
);
connector.setStartX(0);
connector.setStartY(0);
tx = Bindings.createDoubleBinding(() -> bounds.get().getMinX(), bounds);
ty = Bindings.createDoubleBinding(() -> bounds.get().getMinY(), bounds);
connector.endXProperty().bind(tx);
connector.endYProperty().bind(ty);
connector.setStroke(Color.BLACK);
pane.getChildren().add(node);
pane.getChildren().add(connector);
node.relocate(100, 100);
primaryStage.setScene(new Scene(pane, 300, 300));
primaryStage.show();
}
private VBox wrap(Circle target) {
VBox node = new VBox(new Label("Node"), new StackPane(new Rectangle(50, 50, Color.GRAY), target));
node.setOnMousePressed(event -> {
Node source = (Node) event.getSource();
startX = source.getBoundsInParent().getMinX() - event.getScreenX();
startY = source.getBoundsInParent().getMinY() - event.getScreenY();
});
node.setOnMouseDragged(event -> {
Node source = (Node) event.getSource();
double offsetX = event.getScreenX() + startX;
double offsetY = event.getScreenY() + startY;
source.relocate(offsetX, offsetY);
});
return node;
}
}
给定Pane pane
中的任何Node node
(意味着它有一个父级或间接祖先(,基本思想是
ObjectBinding<Bounds> boundsInPaneBinding = Bindings.createObjectBinding(() -> {
Bounds nodeLocal = node.getBoundsInLocal();
Bounds nodeScene = node.localToScene(nodeLocal);
Bounds nodePane = pane.sceneToLocal(nodeScene);
return nodePane ;
}, node.boundsInLocalProperty(), node.localToSceneTransformProperty(),
pane.localToSceneTransformProperty());
则boundsInPaneBinding
是一个ObservableValue<Bounds>
,它总是包含Pane
坐标系中Node
的边界。因此,您可以执行以下操作:
line.startXProperty().bind(Bindings.createDoubleBinding(
() -> boundsInPaneBinding.get().getMinX(),
boundsInPaneBinding));
这里的棘手部分是确保绑定不会过早地被垃圾回收。(有关讨论,请参阅此处。首先,需要保留对绑定的引用:
private ObjectBinding<Bounds> boundsInPaneBinding ;
然后(由于我不太清楚的原因(,绑定实际上必须直接计算绑定属性:
boundsInPaneBinding = Bindings.createObjectBinding(() -> {
Bounds nodeLocal = node.getBoundsInLocal();
// note how this actually gets the value of localToSceneTransformProperty():
Bounds nodeScene = node.getLocalToSceneTransform().apply(nodeLocal);
Bounds nodePane = pane.sceneToLocal(nodeScene);
return nodePane ;
}, node.boundsInLocalProperty(), node.localToSceneTransformProperty(),
pane.localToSceneTransformProperty());