Как сделать так, чтобы панель оставалась за линией, соединяющей draggablenode в javafx - PullRequest
0 голосов
/ 03 сентября 2018

Я проектирую интерфейс графической структуры с перетаскиваемыми узлами. На графике у меня есть компонент, называемый отношением (это панель), который показывает связь между двумя узлами. Я хочу, чтобы отношения остались и двигались вместе со строкой в ​​середине строки.

Текущий дизайн интерфейса, как показано ниже

А ожидаемый такой:

1 Ответ

0 голосов
/ 03 сентября 2018

Вам нужно обновить позицию узла, когда изменяются конечные координаты линии. Чтобы не запускать вычисления несколько раз за проход макета, я рекомендую делать это из метода layoutChildren родительского объекта, но вы также можете сделать это из прослушивателя свойств startX, endY, .... Это приведет к некоторым ненужным вычислениям.

Что касается расчета положения узла: центр узла должен совпадать со средней точкой линии, поэтому вам нужно решить следующее уравнение для markTopLeft:

markTopLeft + (markWidth, markHeight) / 2 = (lineStart + lineEnd) / 2

markTopLeft = (lineStart + lineEnd - (markWidth, markHeight)) / 2

Пример

Панель с возможностью пользовательских вычислений макета

public class PostProcessPane extends Pane {

    private final Set<Node> modifiedChildren = new HashSet<>();
    private final Set<Node> modifiedChildrenUnmodifiable = Collections.unmodifiableSet(modifiedChildren);
    private final List<Consumer<Set<Node>>> postProcessors = new ArrayList<>();

    public List<Consumer<Set<Node>>> getPostProcessors() {
        return postProcessors;
    }

    private final ChangeListener listener = (o, oldValue, newValue) -> modifiedChildren.add((Node) ((ReadOnlyProperty) o).getBean());

    private void initListener() {
        getChildren().addListener((ListChangeListener.Change<? extends Node> c) -> {
            while (c.next()) {
                if (c.wasRemoved()) {
                    for (Node n : c.getRemoved()) {
                        n.boundsInParentProperty().removeListener(listener);
                    }
                }
                if (c.wasAdded()) {
                    for (Node n : c.getAddedSubList()) {
                        n.boundsInParentProperty().addListener(listener);
                    }
                }
            }
        });
    }

    public PostProcessPane() {
        initListener();
    }

    public PostProcessPane(Node... children) {
        super(children);
        initListener();

        for (Node n : children) {
            n.boundsInParentProperty().addListener(listener);
        }
    }

    @Override
    protected void layoutChildren() {
        super.layoutChildren();

        if (!modifiedChildren.isEmpty()) {
            for (Consumer<Set<Node>> processor : postProcessors) {
                processor.accept(modifiedChildrenUnmodifiable);
            }
            modifiedChildren.clear();
        }

    }

}

Использование

@Override
public void start(Stage primaryStage) throws Exception {
    Rectangle r1 = new Rectangle(200, 50, Color.BLUE);
    Rectangle r2 = new Rectangle(200, 50, Color.RED);
    Rectangle mark = new Rectangle(200, 50, Color.YELLOW);
    Line line = new Line();

    r1.setX(20);
    r2.setX(380);
    r2.setY(450);

    PostProcessPane root = new PostProcessPane(line, r1, r2, mark);
    root.getPostProcessors().add(changedNodes -> {
        if (changedNodes.contains(r1) || changedNodes.contains(r2) || changedNodes.contains(mark)) {
            Bounds bounds1 = r1.getBoundsInParent();
            Bounds bounds2 = r2.getBoundsInParent();

            // refresh line ends
            line.setStartX(bounds1.getMinX() + bounds1.getWidth() / 2);
            line.setStartY(bounds1.getMaxY());
            line.setEndX(bounds2.getMinX() + bounds2.getWidth() / 2);
            line.setEndY(bounds2.getMinY());

            // recalculate mark position
            mark.setX((line.getStartX() + line.getEndX() - mark.getWidth()) / 2);
            mark.setY((line.getStartY() + line.getEndY() - mark.getHeight()) / 2);
        }
    });

    // add some movement for the nodes
    Timeline timeline = new Timeline(
            new KeyFrame(Duration.ZERO,
                    new KeyValue(r1.xProperty(), r1.getX()),
                    new KeyValue(r1.yProperty(), r1.getY()),
                    new KeyValue(r2.xProperty(), r2.getX())),
            new KeyFrame(Duration.seconds(1),
                    new KeyValue(r2.xProperty(), r1.getX())),
            new KeyFrame(Duration.seconds(2),
                    new KeyValue(r1.xProperty(), r2.getX()),
                    new KeyValue(r1.yProperty(), r2.getY() / 2))
    );
    timeline.setAutoReverse(true);
    timeline.setCycleCount(Animation.INDEFINITE);
    timeline.play();

    Scene scene = new Scene(root);

    primaryStage.setScene(scene);
    primaryStage.show();
}
...