JavaFX 似乎正在丢失我的事件处理程序吗?
Does JavaFX seem to be losing my event handler?
我是 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。无论如何,第二行保持不变。我不得不认为,不知何故发生的事情是其中一个 EventHandlers 从它们各自的单元格中“拔掉”,或者不知何故其中一条线在内存中丢失了。这是画线的代码(我知道它真的很笨重)。此方法在 Graph class 中,它控制 class 的图形(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();
}
});
}
}
}
}
}
}
如果没有适当的最小可重现示例,很难判断出了什么问题,但您似乎使事情变得比需要的更复杂。如果您希望边缘“链接”到节点,那么我建议您使用绑定。这是一个概念验证:
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
来表示图形节点。
由于您要表示图表,因此您的应用程序会更加复杂。如果图形是动态的并且您希望视图实时更新,那么您需要保留对节点及其关联边的引用以随意添加和删除它们。但请记住,视图只是模型的可视化表示。不要使用视图来存储模型信息(例如实际存在的节点和边)。
我是 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。无论如何,第二行保持不变。我不得不认为,不知何故发生的事情是其中一个 EventHandlers 从它们各自的单元格中“拔掉”,或者不知何故其中一条线在内存中丢失了。这是画线的代码(我知道它真的很笨重)。此方法在 Graph class 中,它控制 class 的图形(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();
}
});
}
}
}
}
}
}
如果没有适当的最小可重现示例,很难判断出了什么问题,但您似乎使事情变得比需要的更复杂。如果您希望边缘“链接”到节点,那么我建议您使用绑定。这是一个概念验证:
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
来表示图形节点。
由于您要表示图表,因此您的应用程序会更加复杂。如果图形是动态的并且您希望视图实时更新,那么您需要保留对节点及其关联边的引用以随意添加和删除它们。但请记住,视图只是模型的可视化表示。不要使用视图来存储模型信息(例如实际存在的节点和边)。