在JavaFX中实现撤销/重做
我试图在JavaFX中实现撤销/重做-我使用在JavaFX中实现撤销/重做,java,javafx,javafx-2,javafx-8,Java,Javafx,Javafx 2,Javafx 8,我试图在JavaFX中实现撤销/重做-我使用graphicsContext()绘制所有形状。我环顾四周,发现在图形上下文中有一个save方法,但它只保存属性,而不是画布的实际形状/状态。最好的办法是什么 这是我创建圆时的代码片段之一,例如: public CircleDraw(Canvas canvas, Scene scene, BorderPane borderPane) { this.borderPane = borderPane; this.scene
graphicsContext()
绘制所有形状。我环顾四周,发现在图形上下文中有一个save
方法,但它只保存属性,而不是画布的实际形状/状态。最好的办法是什么
这是我创建圆时的代码片段之一,例如:
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));
};
}
这里的问题是这样的信息没有保存在
画布中。此外,没有反向操作允许您返回到每个绘图信息的前一状态。当然,您可以在同一个椭圆形上进行笔划,但是可以使用背景色,但是,先前图形信息中的信息可能已被覆盖,例如,如果您正在绘制多个相交的椭圆形
但是,可以使用命令模式存储图形操作。这允许您重新绘制所有内容
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);
}
}
与
undo
和redo
可以浏览历史记录。可能会帮助您解决问题。该库只提供了一个抽象的撤消/重做状态管理器,因此它并不能立即解决问题,您需要在应用程序中使用大量自定义代码才能适当地使用它(例如,fabian解决方案中的EllipseDrawOperation
等内容仍然是必需的UndoFX库只是提供了一个存储和操作此类操作历史的位置)。这非常有帮助,fabian,谢谢-我有一个问题。如果我画另一个圆圈,我会不断出错(在我画完第一个圆圈后)-在这一行抛出:operations.subList(historyIndex+1,operations.size()).clear();
我得到一个:“JavaFX应用程序线程”java.lang.IllegalArgumentException:fromIndex(1)>到Index(0)
。我不太清楚为什么会发生这种情况?@xn139:抱歉,但目前我找不到一系列操作来重现这种情况。此外:修改historyIndex
(undo
/redo
/addDrawOperation
)/列表(addDrawOperation
)大小应确保historyIndex
始终是有效的索引或-1
,因此historyIndex@fabian使用透明颜色绘制其他图形不会改变任何内容。这是透明颜色的目的。作者现在在回答中已更正。(只是为了避免混淆。)@fabian,非常感谢-我已经解决了我遇到的问题。谢谢你的帮助!
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));