问题
I have two nodes. I would like to click on one node, drag my cursor across to another node, and connect the two nodes with a line once the cursor is released.
I have set up all the EventHandlers -- dragging and dropping the line works, but it doesn't bind to the second node.
public class LinkHandler {
static Node hoverNode;
}
This class is used to find what Node the cursor is hovering over.
private void setLinkHandlers(Node node) {
Line line = new Line();
this.getChildren().add(line);
// On mouse hover, set LinkHandler.hoverNode
node.setOnMouseDragEntered( (MouseEvent mouseEvent) -> {
LinkHandler.setHoverNode(node);
System.out.println(node);
});
// On mouse exit, remove LinkHandler.hoverNode
node.setOnMouseDragExited( (MouseEvent mouseEvent) -> {
// PLEASE NOTE: I have tried commenting and uncommenting the line below. Neither works.
// LinkHandler.setHoverNode(null);
});
// On mouse press, set Line startpoint
node.setOnMousePressed( (MouseEvent mouseEvent) -> {
// Stop BorderPane from being dragged
LinkHandler.isConnecting = true;
// Bind startpoint to node position
line.startXProperty().bind(node.layoutXProperty());
line.startYProperty().bind(node.layoutYProperty());
});
// On mouse released, either bind Line to LinkHandler.hoverNode, or remove if hoverNode = null
node.setOnMouseReleased( (MouseEvent mouseEvent) -> {
// Allow BorderPane to be dragged again
LinkHandler.isConnecting = false;
// If there is a node to connect to...
if(LinkHandler.getHoverNode() != null) {
// Bind end position to the node's position
line.endXProperty().bind(LinkHandler.getHoverNode().layoutXProperty());
line.endYProperty().bind(LinkHandler.getHoverNode().layoutYProperty());
} else {
// Otherwise print error
System.out.println("Not hovering over a node. Cannot create connection.");
}
});
// Temporarily bind Line endpoint to mouse position.
node.setOnMouseDragged( (MouseEvent mouseEvent) -> {
line.setEndX(mouseEvent.getX());
line.setEndY(mouseEvent.getY());
});
}
This is a lot of code, so I'll try to summarise it:
- When dragEntered, set LinkHandler.hoverNode to this node
- When dragExit, set LinkHandler.hoverNode to null
- When mousePressed, bind Line start position to this node position
- When mouseDragged, temporarily set Line end position to mousePosition
- When mouseReleased, bind Line end position to LinkerHandler.hoverNode position, or remove Line is hoverNode is null.
I believe the problem lies in this snippet:
// On mouse hover, set LinkHandler.hoverNode
node.setOnMouseDragEntered( (MouseEvent mouseEvent) -> {
LinkHandler.setHoverNode(node);
System.out.println(node);
});
// On mouse exit, remove LinkHandler.hoverNode
node.setOnMouseDragExited( (MouseEvent mouseEvent) -> {
// LinkHandler.setHoverNode(null);
});
I don't think this is getting called properly. The Nodes that this code applies to are children of a BorderPane, which has it's own onMouseDrag functionality << this might also be causing a problem.
Thanks in advance, sorry if this question is a bit vague. I tried to be specific.
回答1:
Note that you never initialize a full drag and therefore the events are never delivered to nodes other that the start node of the drag. To change this, call startFullDrag()
for the start node.
The following example demonstrates snapping the lines to the center of Circle
s:
public static Circle createCircle(double x, double y) {
return new Circle(x, y, 20, Color.BLACK.deriveColor(0, 1, 1, 0.5));
}
@Override
public void start(Stage primaryStage) {
Node[] circles = new Node[]{
createCircle(40, 40),
createCircle(240, 40),
createCircle(40, 240),
createCircle(240, 240)
};
Pane root = new Pane(circles);
class DragStartHandler implements EventHandler<MouseEvent> {
public Line line;
@Override
public void handle(MouseEvent event) {
if (line == null) {
Node sourceNode = (Node) event.getSource();
line = new Line();
Bounds bounds = sourceNode.getBoundsInParent();
// start line at center of node
line.setStartX((bounds.getMinX() + bounds.getMaxX()) / 2);
line.setStartY((bounds.getMinY() + bounds.getMaxY()) / 2);
line.setEndX(line.getStartX());
line.setEndY(line.getStartY());
sourceNode.startFullDrag();
root.getChildren().add(0, line);
}
}
}
DragStartHandler startHandler = new DragStartHandler();
EventHandler<MouseDragEvent> dragReleaseHandler = evt -> {
if (evt.getGestureSource() == evt.getSource()) {
// remove line, if it starts and ends in the same node
root.getChildren().remove(startHandler.line);
}
evt.consume();
startHandler.line = null;
};
EventHandler<MouseEvent> dragEnteredHandler = evt -> {
if (startHandler.line != null) {
// snap line end to node center
Node node = (Node) evt.getSource();
Bounds bounds = node.getBoundsInParent();
startHandler.line.setEndX((bounds.getMinX() + bounds.getMaxX()) / 2);
startHandler.line.setEndY((bounds.getMinY() + bounds.getMaxY()) / 2);
}
};
for (Node n : circles) {
// register handlers
n.setOnDragDetected(startHandler);
n.setOnMouseDragReleased(dragReleaseHandler);
n.setOnMouseDragEntered(dragEnteredHandler);
// add info allowing to identify this node as drag source/target
n.setUserData(Boolean.TRUE);
}
root.setOnMouseReleased(evt -> {
// mouse released outside of a target -> remove line
root.getChildren().remove(startHandler.line);
startHandler.line = null;
});
root.setOnMouseDragged(evt -> {
if (startHandler.line != null) {
Node pickResult = evt.getPickResult().getIntersectedNode();
if (pickResult == null || pickResult.getUserData() != Boolean.TRUE) {
// mouse outside of target -> set line end to mouse position
startHandler.line.setEndX(evt.getX());
startHandler.line.setEndY(evt.getY());
}
}
});
Scene scene = new Scene(root, 280, 280);
primaryStage.setScene(scene);
primaryStage.show();
}
来源:https://stackoverflow.com/questions/39658505/javafx-connecting-two-child-nodes-with-a-line-by-dragging-and-dropping