尝试在 JavaFX 中切换 GraphicsContext 的上下文

Trying to switch the context of GraphicsContext in JavaFX

我正在使用 JavaFX 创建一个小型绘图程序。我已经在 canvas 上成功实现了免费绘图。但是,我还希望能够从我单击的点到按下鼠标时绘制直线。我事先创建了一条线,但是我似乎无法切换上下文,所以我只会画直线而不是自由绘图。

目前,如果我点击我的 "straight line" 按钮,它会绘制一条直线,然后默认返回自由绘图,同时抛出很多错误。你能帮忙吗?

这是我的免费绘图代码:

paintScene.setOnMousePressed(e -> {
        gc.beginPath();
        gc.lineTo(e.getSceneX(), e.getSceneY());
        gc.stroke();
    });

    paintScene.setOnMouseDragged(e -> {
        gc.lineTo(e.getSceneX(), e.getSceneY());
        gc.stroke();
    });    

(其中 gc 是 GraphicsContext)

这是我创建直线的功能:

    Line l = new Line(20, 30, 30, 20);
    l.setStroke(Color.BLACK);
    l.setStrokeWidth(10);

       straightLineBtn.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            paintScene.setOnMousePressed(event1 -> {
              canvasHolder.getChildren().addAll(l);
            });

        }
    });

这一行

canvasHolder.getChildren().addAll(l);

尝试在每次点击时将同一行 l 添加到 canvasHolder

禁止将同一个组件多次添加到组件树中。您应该在每次点击时创建并添加一个新的 Line

您为 自由绘图 模式定义了 onMousePressedonMouseDragged 处理程序,并且只为 直接替换 onMousePressed 模式。这会导致一些模式组合被激活,而不是纯粹的 直线 模式。要解决此问题,您应该删除 onMouseDragged 处理程序,或者以与 onMousePressed 处理程序相同的方式重新定义它。

您需要更新行,直到您松开鼠标按钮。这意味着您要么必须存储有关之前绘制的所有内容的数据,要么需要在 canvas 之上绘制 Line Node。我推荐后一种方法。

示例代码

private static class LineDrawListener implements EventHandler<ActionEvent> {

    private LineDrawListener(Canvas canvas, Pane canvasPane) {
        this.canvasPane = canvasPane;
        this.gc = canvas.getGraphicsContext2D();

        line = new Line();
        line.setStrokeWidth(10);
        line.setManaged(true);
        line.setMouseTransparent(true);

        releasedHandler = evt -> {
            // remove line from canvas parent and draw line on canvas instead
            canvasPane.getChildren().remove(line);
            Point2D start = canvas.parentToLocal(line.getStartX(), line.getStartY());
            Point2D end = canvas.parentToLocal(line.getEndX(), line.getEndY());
            gc.setLineWidth(10);
            gc.strokeLine(start.getX(), start.getY(), end.getX(), end.getY());
            removeListeners();
        };
        draggedHandler = evt -> {
            // update end of line
            line.setEndX(evt.getX());
            line.setEndY(evt.getY());
        };
        pressedHandler = evt -> {
            // add line to canvas parent
            canvasPane.getChildren().add(line);
            line.setStartX(evt.getX());
            line.setStartY(evt.getY());
            line.setEndX(evt.getX());
            line.setEndY(evt.getY());
        };
    }

    private final GraphicsContext gc;
    private final Pane canvasPane;
    private final Line line;

    private final EventHandler<MouseEvent> pressedHandler;

    private final EventHandler<MouseEvent> draggedHandler;

    private final EventHandler<MouseEvent> releasedHandler;

    private void removeListeners() {
        canvasPane.setOnMousePressed(null);
        canvasPane.setOnMouseDragged(null);
        canvasPane.setOnMouseReleased(null);
    }

    @Override
    public void handle(ActionEvent event) {
        canvasPane.setOnMousePressed(pressedHandler);
        canvasPane.setOnMouseDragged(draggedHandler);
        canvasPane.setOnMouseReleased(releasedHandler);
    }

}

@Override
public void start(Stage primaryStage) {
    Canvas canvas = new Canvas(400, 400);

    Rectangle clip = new Rectangle();
    clip.widthProperty().bind(canvas.widthProperty());
    clip.heightProperty().bind(canvas.heightProperty());

    Pane canvasPane = new Pane(canvas);
    canvasPane.setClip(clip);

    Button btn = new Button("Draw Line");
    LineDrawListener listener = new LineDrawListener(canvas, canvasPane);
    btn.setOnAction(listener);

    VBox root = new VBox(btn, canvasPane);

    Scene scene = new Scene(root);

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