Я пытаюсь нарисовать кривую между двумя узлами, которые находятся в другом родительском контейнере.Контейнер может быть произвольно глубоко вложенным.Я могу преобразовать локальные координаты в координатное пространство общего предка, однако границы узлов недоступны во время рисования линии.
В моем приложении я создаю свои узлы в фоновом потокеи добавить их позже на сцену.Поэтому вычисление координат линии должно быть запущено, когда два узла полностью расположены (они должны иметь высоту и ширину).
Я попытался решить проблему с помощью прослушивателя изменения свойства, но не могу найти свойство, которое изменяется достаточно часто.
Есть ли событие, которое я могу прослушать, которое вызывается при необходимостисвойства установлены?
Заранее спасибо!
Я добавил упрощенный пример, который должен показать проблему.Линия отображается правильно, как только вы нажмете кнопку «перерисовать».
Класс приложения:
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Showcase");
Scene scene = new Scene(root, 300, 300);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
sample.fxml
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:controller="sample.Controller" xmlns:fx="http://javafx.com/fxml">
<ToolBar AnchorPane.rightAnchor="0" AnchorPane.leftAnchor="0" prefHeight="50">
<Button onAction="#redrawEdge" text="Redraw Line"/>
</ToolBar>
<AnchorPane fx:id="contentPane" AnchorPane.topAnchor="50"
AnchorPane.leftAnchor="0"
AnchorPane.rightAnchor="0"/>
</AnchorPane>
Контроллер:
public class Controller {
private final Rectangle node1 = new Rectangle();
private final Ellipse node2 = new Ellipse();
private final HBox parent = new HBox();
private final HBox parent2 = new HBox();
private final VBox parentParent = new VBox();
private final Line edge = new Line();
@FXML
private AnchorPane contentPane;
@FXML
public void initialize() {
// not updated as expected
node1.boundsInParentProperty().addListener((o) -> drawLine());
node2.boundsInParentProperty().addListener((o) -> drawLine());
node1.setHeight(20);
node1.setWidth(20);
node1.setFill(Color.BURLYWOOD);
node2.setRadiusX(20);
node2.setRadiusY(20);
node2.setFill(Color.DEEPSKYBLUE);
parent.setStyle("-fx-border-color: #DC143C");
parent.setPadding(new Insets(10));
parent.getChildren().add(node1);
parent2.setStyle("-fx-border-color: #5CD3A9");
parent2.setPadding(new Insets(10));
parent2.getChildren().add(node2);
parentParent.setStyle("-fx-border-color: #0336FF");
parentParent.setLayoutX(200);
parentParent.setLayoutY(50);
parentParent.setPadding(new Insets(10));
parentParent.getChildren().add(parent);
contentPane.setStyle("-fx-border-color: #4B0082;");
contentPane.setPadding(new Insets(10));
contentPane.getChildren().addAll(parentParent, parent2, edge);
}
@FXML
public void redrawEdge(ActionEvent actionEvent) {
drawLine();
}
private void drawLine() {
Bounds n1InCommonAncestor = getRelativeBounds(node1, contentPane);
Bounds n2InCommonAncestor = getRelativeBounds(node2, contentPane);
Point2D n1Center = getCenter(n1InCommonAncestor);
Point2D n2Center = getCenter(n2InCommonAncestor);
Point2D startIntersection = findIntersectionPoint(n1InCommonAncestor,
n1Center, n2Center);
Point2D endIntersection = findIntersectionPoint(n2InCommonAncestor,
n2Center, n1Center);
edge.setStartX(startIntersection.getX());
edge.setStartY(startIntersection.getY());
edge.setEndX(endIntersection.getX());
edge.setEndY(endIntersection.getY());
}
private Bounds getRelativeBounds(Node node, Node relativeTo) {
Bounds nodeBoundsInScene = node.localToScene(node.getBoundsInLocal());
return relativeTo.sceneToLocal(nodeBoundsInScene);
}
private Point2D getCenter(Bounds bounds) {
return new Point2D(bounds.getMinX() + bounds.getWidth() / 2,
bounds.getMinY() + bounds.getHeight() / 2);
}
private Point2D findIntersectionPoint(Bounds nodeBounds,
Point2D inside, Point2D outside) {
Point2D middle = outside.midpoint(inside);
double deltaX = outside.getX() - inside.getX();
double deltaY = outside.getY() - inside.getY();
if (Math.hypot(deltaX, deltaY) < 1.) {
return middle;
} else {
if (nodeBounds.contains(middle)) {
return findIntersectionPoint(nodeBounds, middle, outside);
} else {
return findIntersectionPoint(nodeBounds, inside, middle);
}
}
}
}