JavaFX: How to connect two Nodes by a Line?

前端 未结 1 764
没有蜡笔的小新
没有蜡笔的小新 2020-12-03 00:03

I want to connect two Nodes with a Line (from center of the first to the center of the second).

Initial thoughts:

  • It is assum
相关标签:
1条回答
  • 2020-12-03 00:22

    The code in this response is based on the answer to the question: CubicCurve JavaFX

    The sample below:

    • assumes all nodes involved are siblings.
    • ensures the connecting line is not pickable by invoking setMouseTransparent(true) on the line.
    • updates the line automatically to connect the centers of the two anchor nodes as the anchor nodes are dragged around.

    lines

    import javafx.application.Application;
    import javafx.beans.property.*;
    import javafx.event.EventHandler;
    import javafx.scene.*;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.*;
    import javafx.stage.Stage;
    
    /** Example of dragging anchors around to manipulate a line. */
    public class LineManipulator extends Application {
      public static void main(String[] args) throws Exception { launch(args); }
      @Override public void start(final Stage stage) throws Exception {
        DoubleProperty startX = new SimpleDoubleProperty(100);
        DoubleProperty startY = new SimpleDoubleProperty(100);
        DoubleProperty endX   = new SimpleDoubleProperty(300);
        DoubleProperty endY   = new SimpleDoubleProperty(200);
    
        Anchor start    = new Anchor(Color.PALEGREEN, startX, startY);
        Anchor end      = new Anchor(Color.TOMATO,    endX,   endY);
    
        Line line = new BoundLine(startX, startY, endX, endY);
        stage.setTitle("Line Manipulation Sample");
        stage.setScene(new Scene(new Group(line, start, end), 400, 400, Color.ALICEBLUE));
        stage.show();
      }
    
      class BoundLine extends Line {
        BoundLine(DoubleProperty startX, DoubleProperty startY, DoubleProperty endX, DoubleProperty endY) {
          startXProperty().bind(startX);
          startYProperty().bind(startY);
          endXProperty().bind(endX);
          endYProperty().bind(endY);
          setStrokeWidth(2);
          setStroke(Color.GRAY.deriveColor(0, 1, 1, 0.5));
          setStrokeLineCap(StrokeLineCap.BUTT);
          getStrokeDashArray().setAll(10.0, 5.0);
          setMouseTransparent(true);
        }
      }
    
      // a draggable anchor displayed around a point.
      class Anchor extends Circle { 
        Anchor(Color color, DoubleProperty x, DoubleProperty y) {
          super(x.get(), y.get(), 10);
          setFill(color.deriveColor(1, 1, 1, 0.5));
          setStroke(color);
          setStrokeWidth(2);
          setStrokeType(StrokeType.OUTSIDE);
    
          x.bind(centerXProperty());
          y.bind(centerYProperty());
          enableDrag();
        }
    
        // make a node movable by dragging it around with the mouse.
        private void enableDrag() {
          final Delta dragDelta = new Delta();
          setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent mouseEvent) {
              // record a delta distance for the drag and drop operation.
              dragDelta.x = getCenterX() - mouseEvent.getX();
              dragDelta.y = getCenterY() - mouseEvent.getY();
              getScene().setCursor(Cursor.MOVE);
            }
          });
          setOnMouseReleased(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent mouseEvent) {
              getScene().setCursor(Cursor.HAND);
            }
          });
          setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent mouseEvent) {
              double newX = mouseEvent.getX() + dragDelta.x;
              if (newX > 0 && newX < getScene().getWidth()) {
                setCenterX(newX);
              }  
              double newY = mouseEvent.getY() + dragDelta.y;
              if (newY > 0 && newY < getScene().getHeight()) {
                setCenterY(newY);
              }  
            }
          });
          setOnMouseEntered(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent mouseEvent) {
              if (!mouseEvent.isPrimaryButtonDown()) {
                getScene().setCursor(Cursor.HAND);
              }
            }
          });
          setOnMouseExited(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent mouseEvent) {
              if (!mouseEvent.isPrimaryButtonDown()) {
                getScene().setCursor(Cursor.DEFAULT);
              }
            }
          });
        }
    
        // records relative x and y co-ordinates.
        private class Delta { double x, y; }
      }  
    }
    

    The above code is based on a circle, so it is easy to track the circle's centerX and centerY properties.

    For an arbitrarily shaped node, you can track it's center properties within it's parent using the code below:

    class Center {
        private ReadOnlyDoubleWrapper centerX = new ReadOnlyDoubleWrapper();
        private ReadOnlyDoubleWrapper centerY = new ReadOnlyDoubleWrapper();
    
        public Center(Node node) {
            calcCenter(node.getBoundsInParent());
            node.boundsInParentProperty().addListener(new ChangeListener<Bounds>() {
                @Override public void changed(
                       ObservableValue<? extends Bounds> observableValue, 
                       Bounds oldBounds, 
                       Bounds bounds
                ) {
                    calcCenter(bounds);
                }
            });
        }
    
        private void calcCenter(Bounds bounds) {
            centerX.set(bounds.getMinX() + bounds.getWidth()  / 2);
            centerY.set(bounds.getMinY() + bounds.getHeight() / 2);
        }
    
        ReadOnlyDoubleProperty centerXProperty() {
            return centerX.getReadOnlyProperty();
        }
    
        ReadOnlyDoubleProperty centerYProperty() {
            return centerY.getReadOnlyProperty();
        }
    }
    

    Applying the Center code to the Anchor sample above, you get the following code:

    Anchor start = new Anchor(Color.PALEGREEN, startX, startY);
    Anchor end   = new Anchor(Color.TOMATO,    endX,   endY);
    
    Center startCenter = new Center(start);
    Center endCenter   = new Center(end);
    
    Line line = new BoundLine(
            startCenter.centerXProperty(),
            startCenter.centerYProperty(),
            endCenter.centerXProperty(),
            endCenter.centerYProperty()
    );
    

    If you wanted to track arbitrary nodes in a scene, not just sibling nodes, you might want to look into the node.getLayoutBounds and node.getLocalToSceneTransform functions.

    0 讨论(0)
提交回复
热议问题