2013-11-03 14 views
12

Chcę połączyć dwa Node s z Line (od środka pierwszego do środka drugiego).JavaFX: Jak połączyć dwa węzły za pomocą linii?

myśli wstępne:

  • Zakłada się, że oba węzły istnieją gdzieś na wykresie sceny
  • W Line działa jako dekorator i nie powinny być pickable
  • Jeżeli zmiana NodeBounds The Line powinien zostać zaktualizowany:

Wygląda na to, że potrzebuję pewnych powiązań właściwości złożonych, w tym we właściwych transformacjach przestrzeni współrzędnych.

Jak to osiągnąć? Czy ktoś może wskazać kierunek?

Odpowiedz

17

Kod w tej reakcji polega na odpowiedzi na pytanie: CubicCurve JavaFX

próbka poniżej:

  • zakłada wszystkie węzły zaangażowane są rodzeństwem.
  • zapewnia, że ​​linii łączącej nie można doczepić, wywołując na linii setMouseTransparent(true).
  • aktualizuje linię automatycznie, aby połączyć centra dwóch węzłów zakotwiczonych podczas przeciągania węzłów kotwicy.

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

Powyższy kod jest oparty na kole, więc jest łatwy do śledzenia centerx i Centery właściwości okręgu.

Dla dowolnie ukształtowanej węzła, można śledzić jego właściwości Centrum w to rodzic za pomocą poniższego kodu:

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

stosowania kodeksu Center do próbki Anchor powyżej, można uzyskać następujący kod:

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

Jeśli chcesz śledzić dowolne węzły w scenie, a nie tylko w węzłach siostrzanych, możesz zajrzeć do funkcji node.getLayoutBounds i node.getLocalToSceneTransform.

+2

Dziękuję, jak zawsze, bardzo pomocna! Dodatkowo korzystam teraz z niestandardowych powiązań utworzonych za pomocą 'Bindings.createDoubleBinding (Callable , Observable ...)'. Obliczenia dokonuje się za pomocą opcji 'Możliwość wywołania' przy użyciu ' Node.localToScene (Bounds) 'oraz' Node.sceneToLocal (Bounds) 'w oparciu o granice układu. –