2013-11-03 15 views
12

Nodeの2つをLine(最初の中央から2番目の中央の中央まで)で接続したいと考えています。JavaFX:2つの​​ノードを1行で接続する方法は?

初期の思考は:

  • 両方のノードが
  • デコレータとして Line行為
  • とはpickableすべきではないシーングラフのどこかに存在することが想定される
  • NodeBounds変更した場合は、Line更新する必要があります

私はいくつかの複合プロパティーの束縛を必要とするようです適切な座標空間の変換を行います。

これを行うには?誰も方向を指摘できますか?この応答で

答えて

17

コードは質問への回答に基づいています:CubicCurve JavaFX

以下のサンプル:

  • は、関係するすべてのノードが兄弟である前提としています。
  • は、回線上でsetMouseTransparent(true)を呼び出すことによって、接続可能な回線が選択できないことを保証します。
  • アンカーノードがドラッグされるときに、2つのアンカーノードの中心を自動的に結ぶ線を更新します。

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

上記のコードは、円に基づいて、円のcenterXとcenterY特性を追跡することが容易です。任意形状のノードについては

、あなたはそれが以下のコード使用して親の内中央のプロパティです追跡することができます:

:上記アンカーサンプルにセンターコードを適用

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

を、あなたは以下のコードを取得

兄弟ノードだけでなく、シーン内の任意のノードを追跡する場合は、node.getLayoutBoundsおよびnode.getLocalToSceneTransformの機能を調べるとよいでしょう。

+2

ありがとうございます、いつものように、非常に助かりました!さらに、私は今カスタムバインディングを利用して、 'Bindings.createDoubleBinding(Callable 、Observable ...)'で作成しました。 計算は、 'Node.localToScene(Bounds)'とレイアウト境界に基づく 'Node.sceneToLocal(Bounds)'を使って 'Callable'によって行われます。 –

関連する問題