JavaFx通过拖放连接两个子节点和一行

时间:2016-09-23 10:27:09

标签: java user-interface javafx nodes drag

我有两个节点。我想点击一个节点,将光标拖到另一个节点,并在光标释放后用一条线连接两个节点。

我已经设置了所有的EventHandler - 拖放行工作,但它没有绑定到第二个节点。

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());
    });
}

此类用于查找光标悬停在哪个节点上。

    // 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);
    });

这是很多代码,所以我会尝试总结一下:

  • 当dragEntered时,将LinkHandler.hoverNode设置为此节点
  • 当dragExit时,将LinkHandler.hoverNode设置为null
  • 当mousePressed时,将Line起始位置绑定到此节点位置
  • 当mouseDragged时,暂时将Line end position设置为mousePosition
  • 当mouseReleased时,将Line end position绑定到LinkerHandler.hoverNode位置,或者删除Line,hoverNode为null。

我认为问题出在这个片段中:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">



    <android.support.v7.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        card_view:cardCornerRadius="5dp">


           <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:orientation="vertical">


            <ImageView
                android:layout_gravity="center"
                android:id="@+id/img"
                android:scaleType="fitXY"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <TextView
                android:id="@+id/tv_name"
                android:layout_marginTop="10dp"
                android:layout_marginBottom="10dp"
                android:textSize="18sp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold" />
            <TextView
                android:id="@+id/tv_version"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <TextView
                android:id="@+id/tv_api_level"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

        </LinearLayout>
    </android.support.v7.widget.CardView>
    </LinearLayout>

我不认为这是正确的调用。此代码适用的节点是BorderPane的子节点,它具有自己的onMouseDrag功能&lt;&lt;这也可能导致问题。

先谢谢,对不起,如果这个问题有点模糊。我试图具体。

1 个答案:

答案 0 :(得分:1)

请注意,您永远不会初始化完整拖动,因此事件永远不会传递到拖动的起始节点以外的节点。要更改此设置,请为起始节点调用startFullDrag()

以下示例演示了将行捕捉到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();
}