【问题标题】:Does JavaFX seem to be losing my event handler?JavaFX 是否似乎正在丢失我的事件处理程序?
【发布时间】:2021-06-17 04:34:01
【问题描述】:

我是 JavaFX 的完全新手,对 Java 整体来说还是个新手。我正在设计用于自学项目的无向图的图形表示。现在,我正在尝试使节点可拖动,以便边缘将拉伸以保持与节点的连接。在 2 个节点连接的情况下,我已经实现了这一点。但是,添加三分之一会有些奇怪。

假设我们有这种情况:

Cell testOne = new Cell ("testOne", 123);
Cell testTwo = new Cell ("testTwo", 456);
Cell testThree = new Cell ("testThree", 200);

testOne.addConnection(testTwo);
testOne.addConnection(testThree);

我得到的是三个节点,它们的一般区域随机散布着两条线(值得注意的是,这些节点的位置非常随机)。如果我在 testTwo 或 testThree 周围移动,单线将折衷连接到 testOne。无论如何,第二行保持不变。我不得不认为,不知何故,正在发生的事情是其中一个 EventHandler 正在从它们各自的单元中“拔出”,或者以某种方式其中一条线在内存中丢失了。这是画线的代码(我知道它真的很笨重)。此方法在 Graph 类中,它控制类的图形(oop)表示。 “cells”是存储其所有节点的 ArrayList,“connections”是 Cell 实例中的 arrayList,用于跟踪它所连接的所有节点,“LinesBetween”是一个 HashMap,Cell 实例跟踪一条线是否已经在两个节点之间绘制。

public void drawAndManageEdgeLines(){
        if (cells.size() > 1) { //don't wanna make connections if there's only one cell, or none
            int count = 0;
            for (Cell cell : cells) { // for every cell on the graph
                List<Cell> connectionsList = cell.getConnections(); // look at that cell's connections
                if (!connectionsList.isEmpty()) {// validate that the cell is actually supposed to be connected to something
                    for (Cell connection : connectionsList) { // go through all their connections
                        if (!cell.getLinesBetween().get(connection) && cell.getLinesBetween().get(connection) != null) { //check to see whether there is already a line between them

                            Circle sourceCircle = cell.getCellView();
                            Circle targetCircle = connection.getCellView();

                            Bounds sourceBound = sourceCircle.localToScene(sourceCircle.getBoundsInLocal());
                            Bounds targetBound = targetCircle.localToScene(targetCircle.getBoundsInLocal());

                            double targetX = targetBound.getCenterX();
                            double targetY = targetBound.getCenterY();
                            double sourceX = sourceBound.getCenterX();
                            double sourceY = sourceBound.getCenterY();

                            edge = new Line(sourceX, sourceY, targetX, targetY);
                            edge.setStroke(Color.BLACK);
                            edge.setStrokeWidth(2);

                            getChildren().add(edge);

                            edge.toBack();
                            cell.setLinesBetweenEntry(connection, true);
                            connection.setLinesBetweenEntry(cell, true);
                            // these handlers control where the line is dragged to 
                            cell.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
                                @Override
                                public void handle(MouseEvent e) {
                                    edge.setStartX(e.getSceneX()); //this is a really cool method
                                    edge.setStartY(e.getSceneY());
                                    e.consume();
                                }
                            });
                            System.out.println("on round " + count + " we got there: ");
                            connection.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
                                @Override
                                public void handle(MouseEvent e) {
                                    edge.setEndX(e.getSceneX());
                                    edge.setEndY(e.getSceneY());
                                    e.consume();
                                }
                            });
                        }
                    }
                }
            }
        }
    }

【问题讨论】:

  • 我发布了我可以对问题代码所在位置做出的最准确的猜测。
  • 这并不意味着它是minimal reproducible example(←阅读链接的帮助页面了解更多信息)。
  • 另外,与其尝试在事件处理程序中移动边缘,不如将它们绑定到适当的节点位置可能更容易。然后,当您移动节点时,正确的边会自动更新。
  • 谢谢,我正在考虑为绑定创建属性,但是当我看到 setEndX/Y 方法时,我认为我已经作弊了。然后我是否必须调用 relocate() 或其他东西才能在显示屏上更新?抱歉,StackOverFlow 的礼仪很差,显然还在学习。

标签: java javafx


【解决方案1】:

如果没有适当的最小可重现示例,很难判断出了什么问题,但您似乎使这变得比需要的更复杂。如果您希望边缘“链接”到节点,那么我建议您使用绑定。这是一个概念验证:

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

public class Main extends Application {

  @Override
  public void start(Stage primaryStage) {
    Circle node1 = new Circle(100, 100, 50, Color.FIREBRICK);
    Circle node2 = new Circle(900, 550, 50, Color.DARKBLUE);
    Circle node3 = new Circle(900, 100, 50, Color.DARKGREEN);

    addDragHandling(node1);
    addDragHandling(node2);
    addDragHandling(node3);

    Line edge1 = createEdge(node1, node2);
    Line edge2 = createEdge(node1, node3);

    Pane root = new Pane(edge1, edge2, node1, node2, node3);
    primaryStage.setScene(new Scene(root, 1000, 650));
    primaryStage.show();
  }

  private Line createEdge(Circle from, Circle to) {
    Line edge = new Line();
    edge.setStrokeWidth(2);
    edge.startXProperty().bind(from.centerXProperty());
    edge.startYProperty().bind(from.centerYProperty());
    edge.endXProperty().bind(to.centerXProperty());
    edge.endYProperty().bind(to.centerYProperty());
    return edge;
  }

  private void addDragHandling(Circle circle) {
    // changing cursors not necessary, only included to help indicate
    // when something can be dragged and is being dragged
    circle
        .cursorProperty()
        .bind(
            Bindings.when(circle.pressedProperty())
                .then(Cursor.CLOSED_HAND)
                .otherwise(Cursor.OPEN_HAND));

    double[] offset = {0, 0}; // (x, y)
    circle.setOnMousePressed(
        e -> {
          offset[0] = e.getX() - circle.getCenterX();
          offset[1] = e.getY() - circle.getCenterY();
          e.consume();
        });
    circle.setOnMouseDragged(
        e -> {
          circle.setCenterX(e.getX() - offset[0]);
          circle.setCenterY(e.getY() - offset[1]);
          e.consume();
        });
  }
}

请注意,我首先将边缘添加到Pane,以便将它们绘制在节点下。见Z-Order in JavaFX。此外,根据您用来表示图形节点的 Node,您的拖动逻辑可能看起来不同。

由于您要表示一个图形,您的应用程序将更加复杂。如果图形是动态的,并且您希望视图实时更新,那么您需要保留对节点及其关联边的引用,以便随意添加和删除它们。但请记住,视图只是模型的视觉表示。不要使用视图来存储模型信息(例如实际存在哪些节点和边)。

【讨论】:

  • 非常感谢。除了为实现它们而构建的特定方法之外,我还很难看到项目的整体设计,而且我从您的示例中看到,使节点扩展窗格增加了比它的价值更多的心痛(从好的方面来说,它迫使我查看 Bounds 源代码并学习一些有趣的东西)。从这里开始,我的计划显然是填写 GUI,但还要在它和我已经从我的数据文件构建的邻接矩阵之间建立一座桥梁。再次感谢您的指导。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-16
  • 1970-01-01
  • 2012-12-16
  • 2017-12-16
  • 2022-09-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多