JavaFx connecting two child nodes with a line by dragging and dropping

£可爱£侵袭症+ 提交于 2020-01-05 05:42:06

问题


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 Circles:

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!