JavaFX:如何通过线路连接两个节点? [英] JavaFX: How to connect two Nodes by a Line?

查看:142
本文介绍了JavaFX:如何通过线路连接两个节点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用连接两个节点(从第一个中心到中心位置第二个)。



初步想法:




  • 假设两个节点都存在场景图中的某个地方

  • 充当装饰者,不应该被挑选

  • 如果节点 Bounds 更改,应该是已更新



看起来我需要一些复合属性绑定,包括正确的坐标空间转换。



如何实现这一目标?任何人都可以指出方向吗?

解决方案

此响应中的代码基于问题的答案:功能。


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

Initial thoughts:

  • It is assumed that both nodes exist somewhere in the scene graph
  • The Line acts as a decorator and should not be pickable
  • If the Node Bounds change, the Line should be updated

It looks like I will need some compound property bindings including the proper coordinate space transformations.

How to achieve this? Can anyone point out a direction?

解决方案

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.

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.

这篇关于JavaFX:如何通过线路连接两个节点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆