Reputation: 2411
How do you draw a line between the centers of translated nodes? Given for example the following code snippet:
public class Test extends Application{
@Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Circle circle1=new Circle(10, Color.GREEN);
root.getChildren().add(circle1);
Circle circle2=new Circle(10, Color.RED);
root.getChildren().add(circle2);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
circle1.setTranslateX(100);
Line line=new Line(circle1.getCenterX(), circle1.getCenterY(), circle2.getCenterX(), circle2.getCenterY());
root.getChildren().add(line);
}
public static void main(String[] args) {
launch(args);
}
}
Running this application will clearly show a red and a green circle. However, there won't be a line because each of the centers of the circles are at the coordinates (0,0). Nevertheless, the circles do not cover each other because one of the circles is translated. This doesn't work:
Line line=new Line(circle1.getCenterX()+circle1.getTranslateX(), circle1.getCenterY()+circle1.getTranslateY(), circle2.getCenterX()+circle2.getTranslateX(), circle2.getCenterY()+circle2.getTranslateY());
Finally, let's assume that there is an approach to draw a line connecting the centers of the two circles. If, after the line is drawn, I would invoke circle2.setTranslateX(50);
, how do I ensure that the endpoint of the line on the side of circle2 moves accordingly?
Upvotes: 1
Views: 5217
Reputation: 209299
A StackPane
is a managed layout pane, meaning that it manages the positions of its child nodes (by default it centers them); the translation is applied after the StackPane
positions the nodes. This is why the circles appear in different locations but the line is not where you expect. Using a Pane
instead of a StackPane
will make things work as you expect.
To keep the line in the correct position relative to the circles when the circles are repositioned dynamically, bind the startX
, startY
, endX
, and endY
properties, instead of just setting them.
import javafx.animation.Animation;
import javafx.animation.ParallelTransition;
import javafx.animation.SequentialTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;
public class LineConnectingCircles extends Application{
@Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Circle circle1=new Circle(10, Color.GREEN);
root.getChildren().add(circle1);
Circle circle2=new Circle(10, Color.RED);
root.getChildren().add(circle2);
// move circles so we can see them:
circle1.setTranslateX(100);
circle2.setTranslateY(50);
Line line = new Line();
// bind ends of line:
line.startXProperty().bind(circle1.centerXProperty().add(circle1.translateXProperty()));
line.startYProperty().bind(circle1.centerYProperty().add(circle1.translateYProperty()));
line.endXProperty().bind(circle2.centerXProperty().add(circle2.translateXProperty()));
line.endYProperty().bind(circle2.centerYProperty().add(circle2.translateYProperty()));
root.getChildren().add(line);
// create some animations for the circles to test the line binding:
Button button = new Button("Animate");
TranslateTransition circle1Animation = new TranslateTransition(Duration.seconds(1), circle1);
circle1Animation.setByY(150);
TranslateTransition circle2Animation = new TranslateTransition(Duration.seconds(1), circle2);
circle2Animation.setByX(150);
ParallelTransition animation = new ParallelTransition(circle1Animation, circle2Animation);
animation.setAutoReverse(true);
animation.setCycleCount(2);
button.disableProperty().bind(animation.statusProperty().isEqualTo(Animation.Status.RUNNING));
button.setOnAction(e -> animation.play());
BorderPane.setAlignment(button, Pos.CENTER);
BorderPane.setMargin(button, new Insets(10));
Scene scene = new Scene(new BorderPane(root, null, null, button, null), 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Upvotes: 4