如何在 JavaFX 场景图中拖动位于另一个 `shape` 下方的 `Shape`?
How to drag a `Shape` that is below anther `shape` in JavaFX scene graph?
我正在使用 JavaFX 编写一个教育应用程序,用户可以在其中绘制和操作贝塞尔曲线 Line
、QuadCurve
和 CubicCurve
。这些曲线应该能够用鼠标拖动。我有两种选择:
1- 使用classes Line
、QuadCurve
、CubicCurve
,然后用透明色填充,并用另一种颜色描边,比如黑色.该选项出现的问题是用户想要拖动一条曲线,但看到拖动的是另一条曲线。这样做的原因是用户要拖动的曲线位于场景图中另一条曲线的下方。例如,在下图中,较小的曲线无法拖动,因为它在场景图中位于较大曲线的下方。
2- 使用 class javafx.scene.shape.Path
,在这种情况下,问题是操纵这样的路径有点复杂,因为它由一些 PathElement
组成,并且简单地操作元素不会改变 Path
,除非我们从其元素 属性 中删除一个元素并添加一个新元素。因此我更喜欢方法 1.
如何克服第一种方法中出现的问题?
提前感谢您的帮助。我的程序代码简化版如下
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.CubicCurve;
import javafx.scene.shape.QuadCurve;
import javafx.stage.Stage;
public class SampleForWhosebug extends Application
{
double lastMouseX;
double lastMouseY;
double lastTranslateX;
double lastTranslateY;
@Override
public void start(Stage window)
{
final double STROKE_WIDTH = 5;
QuadCurve quad = new QuadCurve(100, 200, 150, 50, 200, 200);
quad.setFill(Color.TRANSPARENT);
quad.setStroke(Color.BLACK);
quad.setStrokeWidth(STROKE_WIDTH);
quad.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = quad.getTranslateX();
lastTranslateY = quad.getTranslateY();
});
quad.setOnMouseDragged(e -> followMouse(quad, e));
CubicCurve cubic = new CubicCurve(0, 300, 100, 0, 300, 0, 300, 300);
cubic.setFill(Color.TRANSPARENT);
cubic.setStroke(Color.BLACK);
cubic.setStrokeWidth(STROKE_WIDTH);
cubic.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = cubic.getTranslateX();
lastTranslateY = cubic.getTranslateY();
});
cubic.setOnMouseDragged(e -> followMouse(cubic, e));
Group root = new Group(quad, cubic);
Scene scene = new Scene(root, 500, 500);
window.setScene(scene);
window.show();
}
private void followMouse(Node node, MouseEvent e)
{
double deltaX = e.getSceneX() - lastMouseX;
double deltaY = e.getSceneY() - lastMouseY;
node.setTranslateX(deltaX + lastTranslateX);
node.setTranslateY(deltaY + lastTranslateY);
}
public static void main(String[] args) {
launch(args);
}
}
使用路径创建 QuadCurve 和 CubicCurve 似乎对我来说效果很好。这是一个完整的例子:
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.QuadCurveTo;
import javafx.stage.Stage;
public class SampleForWhosebug extends Application {
private final double STROKE_WIDTH = 5;
private double lastMouseX;
private double lastMouseY;
private double lastTranslateX;
private double lastTranslateY;
@Override
public void start(Stage window) {
Path quad = initQuadCurve(100, 200, 200, 200, 150, 50);
Path cubic = initCubicCurve(0, 300, 300, 300, 100, 0, 300, 0);
Pane root = new Pane(cubic, quad);
Scene scene = new Scene(root, 500, 500);
window.setScene(scene);
window.show();
}
private Path initQuadCurve(int xStart, int yStart, int xEnd, int yEnd, int controlX, int controlY) {
Path curvePath = new Path();
curvePath.setStrokeWidth(STROKE_WIDTH);
MoveTo moveTo = new MoveTo(xStart, yStart);
QuadCurveTo quadTo = new QuadCurveTo();
quadTo.setControlX(controlX);
quadTo.setControlY(controlY);
quadTo.setX(xEnd);
quadTo.setY(yEnd);
curvePath.getElements().addAll(moveTo, quadTo);
curvePath.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = curvePath.getTranslateX();
lastTranslateY = curvePath.getTranslateY();
});
curvePath.setOnMouseDragged(e -> followMouse(curvePath, e));
return curvePath;
}
private Path initCubicCurve(int xStart, int yStart, int xEnd, int yEnd, int x1Control, int y1Control, int x2Control,
int y2Control) {
Path curvePath = new Path();
curvePath.setStrokeWidth(STROKE_WIDTH);
MoveTo moveTo = new MoveTo(xStart, yStart);
CubicCurveTo cubicTo = new CubicCurveTo();
cubicTo.setControlX1(x1Control);
cubicTo.setControlY1(y1Control);
cubicTo.setControlX2(x2Control);
cubicTo.setControlY2(y2Control);
cubicTo.setX(xEnd);
cubicTo.setY(yEnd);
curvePath.getElements().addAll(moveTo, cubicTo);
curvePath.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = curvePath.getTranslateX();
lastTranslateY = curvePath.getTranslateY();
});
curvePath.setOnMouseDragged(e -> followMouse(curvePath, e));
return curvePath;
}
private void followMouse(Node node, MouseEvent e) {
double deltaX = e.getSceneX() - lastMouseX;
double deltaY = e.getSceneY() - lastMouseY;
node.setTranslateX(deltaX + lastTranslateX);
node.setTranslateY(deltaY + lastTranslateY);
}
public static void main(String[] args) {
launch(args);
}
}
老实说,我的第一次尝试是按照下面两个 post 的建议调用 quad.setPickOnBounds(false);
(以及立方体):
JavaFX 通过透明节点将 MouseEvents 传递给子节点
但它不起作用或者我遗漏了一些东西,但是如果你自己创建路径可行,我会发现没有必要让事情变得更复杂。无论如何,如果您想遵循第一种方法,我建议您查看链接。如果您要遵循第二种方法,我认为操纵路径不会很困难。
您应该明确地将曲线的填充颜色设置为 null 并将 pickOnBounds 设置为 false,而不是在您的第一个场景中使用透明颜色。透明颜色仍会捕获鼠标事件,但 null 不会,当 pickOnBounds 为 false 时,仅当您正好位于形状的彩色部分上方时才会捕获鼠标事件。
我正在使用 JavaFX 编写一个教育应用程序,用户可以在其中绘制和操作贝塞尔曲线 Line
、QuadCurve
和 CubicCurve
。这些曲线应该能够用鼠标拖动。我有两种选择:
1- 使用classes Line
、QuadCurve
、CubicCurve
,然后用透明色填充,并用另一种颜色描边,比如黑色.该选项出现的问题是用户想要拖动一条曲线,但看到拖动的是另一条曲线。这样做的原因是用户要拖动的曲线位于场景图中另一条曲线的下方。例如,在下图中,较小的曲线无法拖动,因为它在场景图中位于较大曲线的下方。
2- 使用 class javafx.scene.shape.Path
,在这种情况下,问题是操纵这样的路径有点复杂,因为它由一些 PathElement
组成,并且简单地操作元素不会改变 Path
,除非我们从其元素 属性 中删除一个元素并添加一个新元素。因此我更喜欢方法 1.
如何克服第一种方法中出现的问题?
提前感谢您的帮助。我的程序代码简化版如下
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.CubicCurve;
import javafx.scene.shape.QuadCurve;
import javafx.stage.Stage;
public class SampleForWhosebug extends Application
{
double lastMouseX;
double lastMouseY;
double lastTranslateX;
double lastTranslateY;
@Override
public void start(Stage window)
{
final double STROKE_WIDTH = 5;
QuadCurve quad = new QuadCurve(100, 200, 150, 50, 200, 200);
quad.setFill(Color.TRANSPARENT);
quad.setStroke(Color.BLACK);
quad.setStrokeWidth(STROKE_WIDTH);
quad.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = quad.getTranslateX();
lastTranslateY = quad.getTranslateY();
});
quad.setOnMouseDragged(e -> followMouse(quad, e));
CubicCurve cubic = new CubicCurve(0, 300, 100, 0, 300, 0, 300, 300);
cubic.setFill(Color.TRANSPARENT);
cubic.setStroke(Color.BLACK);
cubic.setStrokeWidth(STROKE_WIDTH);
cubic.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = cubic.getTranslateX();
lastTranslateY = cubic.getTranslateY();
});
cubic.setOnMouseDragged(e -> followMouse(cubic, e));
Group root = new Group(quad, cubic);
Scene scene = new Scene(root, 500, 500);
window.setScene(scene);
window.show();
}
private void followMouse(Node node, MouseEvent e)
{
double deltaX = e.getSceneX() - lastMouseX;
double deltaY = e.getSceneY() - lastMouseY;
node.setTranslateX(deltaX + lastTranslateX);
node.setTranslateY(deltaY + lastTranslateY);
}
public static void main(String[] args) {
launch(args);
}
}
使用路径创建 QuadCurve 和 CubicCurve 似乎对我来说效果很好。这是一个完整的例子:
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.QuadCurveTo;
import javafx.stage.Stage;
public class SampleForWhosebug extends Application {
private final double STROKE_WIDTH = 5;
private double lastMouseX;
private double lastMouseY;
private double lastTranslateX;
private double lastTranslateY;
@Override
public void start(Stage window) {
Path quad = initQuadCurve(100, 200, 200, 200, 150, 50);
Path cubic = initCubicCurve(0, 300, 300, 300, 100, 0, 300, 0);
Pane root = new Pane(cubic, quad);
Scene scene = new Scene(root, 500, 500);
window.setScene(scene);
window.show();
}
private Path initQuadCurve(int xStart, int yStart, int xEnd, int yEnd, int controlX, int controlY) {
Path curvePath = new Path();
curvePath.setStrokeWidth(STROKE_WIDTH);
MoveTo moveTo = new MoveTo(xStart, yStart);
QuadCurveTo quadTo = new QuadCurveTo();
quadTo.setControlX(controlX);
quadTo.setControlY(controlY);
quadTo.setX(xEnd);
quadTo.setY(yEnd);
curvePath.getElements().addAll(moveTo, quadTo);
curvePath.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = curvePath.getTranslateX();
lastTranslateY = curvePath.getTranslateY();
});
curvePath.setOnMouseDragged(e -> followMouse(curvePath, e));
return curvePath;
}
private Path initCubicCurve(int xStart, int yStart, int xEnd, int yEnd, int x1Control, int y1Control, int x2Control,
int y2Control) {
Path curvePath = new Path();
curvePath.setStrokeWidth(STROKE_WIDTH);
MoveTo moveTo = new MoveTo(xStart, yStart);
CubicCurveTo cubicTo = new CubicCurveTo();
cubicTo.setControlX1(x1Control);
cubicTo.setControlY1(y1Control);
cubicTo.setControlX2(x2Control);
cubicTo.setControlY2(y2Control);
cubicTo.setX(xEnd);
cubicTo.setY(yEnd);
curvePath.getElements().addAll(moveTo, cubicTo);
curvePath.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = curvePath.getTranslateX();
lastTranslateY = curvePath.getTranslateY();
});
curvePath.setOnMouseDragged(e -> followMouse(curvePath, e));
return curvePath;
}
private void followMouse(Node node, MouseEvent e) {
double deltaX = e.getSceneX() - lastMouseX;
double deltaY = e.getSceneY() - lastMouseY;
node.setTranslateX(deltaX + lastTranslateX);
node.setTranslateY(deltaY + lastTranslateY);
}
public static void main(String[] args) {
launch(args);
}
}
老实说,我的第一次尝试是按照下面两个 post 的建议调用 quad.setPickOnBounds(false);
(以及立方体):
JavaFX 通过透明节点将 MouseEvents 传递给子节点
但它不起作用或者我遗漏了一些东西,但是如果你自己创建路径可行,我会发现没有必要让事情变得更复杂。无论如何,如果您想遵循第一种方法,我建议您查看链接。如果您要遵循第二种方法,我认为操纵路径不会很困难。
您应该明确地将曲线的填充颜色设置为 null 并将 pickOnBounds 设置为 false,而不是在您的第一个场景中使用透明颜色。透明颜色仍会捕获鼠标事件,但 null 不会,当 pickOnBounds 为 false 时,仅当您正好位于形状的彩色部分上方时才会捕获鼠标事件。