Reputation: 107
I have a Pane where I add 2 Circle. I want to link these Circles with a Line and have the Line moving with the Circle ( they are draggable ).
draw_zone is a Pane.
public void initialize(URL url, ResourceBundle rb) {
vertex_list = new ArrayList<Circle>();
edge_list = new ArrayList<Edge>();
draw_zone.addEventHandler(MouseEvent.MOUSE_PRESSED, add_vertex_handler);
Circle c1 = new Circle(20,50,5);
Circle c2 = new Circle(100,120,5);
c1.setFill(Color.RED);
c2.setFill(Color.RED);
edge_list.add(new Edge(c1,c2));
Utilitaires.makeDraggable(c1);
Utilitaires.makeDraggable(c2);
draw_zone.getChildren().addAll(c1,c2,edge_list.get(0).line);
}
The binding is made in this class:
public class Edge {
public Line line;
public Circle c1;
public Circle c2;
public Edge(Circle c1, Circle c2){
this.c1 = c1;
this.c2 = c2;
this.line = this.connect(c1, c2);
}
private Line connect(Circle c1, Circle c2) {
Line line = new Line();
line.startXProperty().bind(c1.centerXProperty());
line.startYProperty().bind(c1.centerYProperty());
line.endXProperty().bind(c2.centerXProperty());
line.endYProperty().bind(c2.centerYProperty());
line.setStrokeWidth(1);
//line.setStrokeLineCap(StrokeLineCap.BUTT);
//line.getStrokeDashArray().setAll(1.0, 4.0);
return line;
}
The result I get is 2 Circle linked with a Line, but when i drag 1 Circle, the Line doesn't move at all.
EDIT: Here is the makeDraggable method
public static void makeDraggable(Node node) {
class T {
double initialTranslateX, initialTranslateY,
anchorX, anchorY;
}
final T t = new T();
if (node == null) {
System.err.println("makeDraggable node == null");
}
node.setOnMousePressed(event -> {
event.consume();
t.initialTranslateX = node.getTranslateX();
t.initialTranslateY = node.getTranslateY();
Point2D point = node.localToParent(event.getX(), event.getY());
t.anchorX = point.getX();
t.anchorY = point.getY();
});
node.setOnMouseDragged(event -> {
Point2D point = node.localToParent(event.getX(), event.getY());
node.setTranslateX(t.initialTranslateX - t.anchorX + point.getX());
node.setTranslateY(t.initialTranslateY - t.anchorY + point.getY());
});
}
Upvotes: 0
Views: 277
Reputation: 2917
I will try to explain with my poor English what is going on here. Every Node has some properties which handle the location and the dimensions of the node. In order to layout the node in a specific location ( assuming you are using a layout like AnchorPane or Pane which are not handling the nodes with specific layout rules ) you will have to set the layoutX and layoutY properties of the node. In order to access those properties you need to use one of the methods below :
layoutXProperty()
/ layoutYProperty()
: which returns the properties.setLayoutX(value)
/ setLayoutY(value)
: which sets the new values to the propertiesrelocate(xValue,yValue)
: calling the setLayoutX and Y Now instead of setting the node location you can change the Node transformation. For example instead of saying I will place the node A to x,y = 100,50 you can say I will set the Node to 0,0 but i will move the x transformation by 100 and the y by 50. So the layoutX and layoutY will stay at 0 but the transformation properties will have 100 and 50 for X and Y. Those properties are translateX , translateY and translateZ which you can access using :
setTranslateX(value)
/ setTranslateY(value)
translateXProperty()
/ translateYProperty()
: which returns the actual propertySo if everything is clear you can imagine that binding an object (A) to a layoutXProperty / layoutYProperty of an object (B) and then increase the translateXProperty / translateYProperty of the B object will change nothing to the A object.
Now let's talk about the Circle. Among all the node properties the Circle has one more which is centerX and centerY. From the name of the properties, you can actually understand their usage. Now like the transformation properties and the layout properties the centerX and centerY are also independent of the others. So if you change the location of a node by changing its transformation it will not affect the values of the centerX/centerY properties. Lastly by creating a new Circle using the constructor Circle(double centerX, double centerY, double radius)
you are actually setting the centerX and centerY properties and leaving the others to 0 ( layoutX,layoutY, translateX , translateY )
Conclusion: you are binding the Line using the centerX and centerY but you are updating the transformation properties ( translateX , translateY ) so that's why the line stays in the same location. I will recommend changing the makeDraggable method like :
static class Delta {
double x, y;
}
final Delta dragDelta = new Delta();
private void makeDraggable(Circle circle) {
circle.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent mouseEvent) {
dragDelta.x = circle.getCenterX() - mouseEvent.getX();
dragDelta.y = circle.getCenterY() - mouseEvent.getY();
}
});
circle.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent mouseEvent) {
circle.setCenterX(mouseEvent.getX() + dragDelta.x);
circle.setCenterY(mouseEvent.getY() + dragDelta.y);
}
});
}
I hope everything is clear enough to make my point :P
Full example :
ShapesTest.java
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class ShapesTest extends Application {
public static void main(String[] args) {
launch(args);
}
private AnchorPane root;
@Override
public void start(Stage stage) {
root = new AnchorPane();
Circle c1 = new Circle(120, 50, 20);
Circle c2 = new Circle(300, 120, 20);
c1.setFill(Color.RED);
c2.setFill(Color.RED);
makeDraggable(c1);
makeDraggable(c2);
Edge edj = new Edge(c1, c2);
root.getChildren().addAll(edj.line, c1, c2);
Scene scene = new Scene(root, 500, 500);
stage.setScene(scene);
stage.show();
}
static class Delta {
double x, y;
}
final Delta dragDelta = new Delta();
private void makeDraggable(Circle circle) {
circle.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent mouseEvent) {
dragDelta.x = circle.getCenterX() - mouseEvent.getX();
dragDelta.y = circle.getCenterY() - mouseEvent.getY();
}
});
circle.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent mouseEvent) {
circle.setCenterX(mouseEvent.getX() + dragDelta.x);
circle.setCenterY(mouseEvent.getY() + dragDelta.y);
}
});
}
}
And the Edge.java its the same as yours
Upvotes: 1