在JavaFX中实现撤消/重做 [英] Implementing Undo/Redo in JavaFX

查看:76
本文介绍了在JavaFX中实现撤消/重做的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在JavaFX中实现撤消/重做-我使用graphicsContext()绘制了所有形状.我环顾四周,发现Graphics Context上有一个save方法,但它只保存属性,而不是画布的实际形状/状态.最好的方法是什么?

I'm trying to implement undo/redo in JavaFX - I draw all my shapes using graphicsContext(). I have looked around and found that there's a save method on Graphics Context but it just saves attributes and not the actual shape/state of the canvas. What would be the best way of going about this?

这是我创建圆圈时的代码片段之一,例如:

This is one of my code snippets when I create a circle, for instance:

 public CircleDraw(Canvas canvas, Scene scene, BorderPane borderPane) {
        this.borderPane = borderPane;
        this.scene = scene;
        this.graphicsContext = canvas.getGraphicsContext2D();

        ellipse = new Ellipse();
        ellipse.setStrokeWidth(1.0);
        ellipse.setFill(Color.TRANSPARENT);
        ellipse.setStroke(Color.BLACK);

        pressedDownMouse = event -> {
            startingPosX = event.getX();
            startingPosY = event.getY();
            ellipse.setCenterX(startingPosX);
            ellipse.setCenterY(startingPosY);
            ellipse.setRadiusX(0);
            ellipse.setRadiusY(0);
            borderPane.getChildren().add(ellipse);

        };

        releasedMouse = event -> {
            borderPane.getChildren().remove(ellipse);
            double width = Math.abs(event.getX() - startingPosX);
            double height = Math.abs(event.getY() - startingPosY);
            graphicsContext.setStroke(Color.BLACK);
            graphicsContext.strokeOval(Math.min(startingPosX, event.getX()),    Math.min(startingPosY, event.getY()), width, height);
        removeListeners();
        };

        draggedMouse = event -> {
            ellipse.setCenterX((event.getX() + startingPosX) / 2);
            ellipse.setCenterY((event.getY() + startingPosY) / 2);
            ellipse.setRadiusX(Math.abs((event.getX() - startingPosX) / 2));
            ellipse.setRadiusY(Math.abs((event.getY() - startingPosY) / 2));

        };

    }

推荐答案

这里的问题是这样的信息没有保存在Canvas中.此外,没有逆运算可让您针对每个绘图信息返回到先前的状态.当然,您可以画出相同的椭圆形,但带有backgrund颜色,但是先前图形信息中的信息可能已被覆盖,例如如果要绘制多个相交的椭圆.

The problem here is that there is that information like this is not saved in a Canvas. Furthermore there is no inverse operation that allows you to get back to the previous state for every draw information. Surely you could stroke the same oval, but with backgrund color, however the information from previous drawing information could have been overwritten, e.g. if you're drawing multiple intersecting ovals.

但是,您可以使用命令模式来存储绘图操作.这样您就可以重新绘制所有内容.

You could store the drawing operations using the command pattern however. This allows you to redraw everything.

public interface DrawOperation {
    void draw(GraphicsContext gc);
}

public class DrawBoard {
    private final List<DrawOperation> operations = new ArrayList<>();
    private final GraphicsContext gc;
    private int historyIndex = -1;

    public DrawBoard(GraphicsContext gc) {
        this.gc = gc;
    }

    public void redraw() {
        Canvas c = gc.getCanvas();
        gc.clearRect(0, 0, c.getWidth(), c.getHeight());
        for (int i = 0; i <= historyIndex; i++) {
            operations.get(i).draw(gc);
        }
    }

    public void addDrawOperation(DrawOperation op) {
        // clear history after current postion
        operations.subList(historyIndex+1, operations.size()).clear();

        // add new operation
        operations.add(op);
        historyIndex++;
        op.draw(gc);
    }

    public void undo() {
        if (historyIndex >= 0) {
            historyIndex--;
            redraw();
        }
    }

    public void redo() {
        if (historyIndex < operations.size()-1) {
            historyIndex++;
            operations.get(historyIndex).draw(gc);
        }
    }
}

class EllipseDrawOperation implements DrawOperation {

    private final double minX;
    private final double minY;
    private final double width;
    private final double height;
    private final Paint stroke;

    public EllipseDrawOperation(double minX, double minY, double width, double height, Paint stroke) {
        this.minX = minX;
        this.minY = minY;
        this.width = width;
        this.height = height;
        this.stroke = stroke;
    }

    @Override
    public void draw(GraphicsContext gc) {
        gc.setStroke(stroke);
        gc.strokeOval(minX, minY, width, height);
    }

}

DrawBoard实例而不是Canvas传递给您的班级并替换

Pass a DrawBoard instance to your class instead of the Canvas and replace

graphicsContext.setStroke(Color.BLACK);
graphicsContext.strokeOval(Math.min(startingPosX, event.getX()),    Math.min(startingPosY, event.getY()), width, height);

使用

drawBoard.addDrawOperation(new EllipseDrawOperation(
                             Math.min(startingPosX, event.getX()),
                             Math.min(startingPosY, event.getY()),
                             width,
                             height,
                             Color.BLACK));

通过undoredo浏览历史记录.

这篇关于在JavaFX中实现撤消/重做的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆