撤销和重做功能在很多软件都是非常普遍的,这里记录一下JavaFX实现撤销和重做功能的一种方案:
1. 接口定义
对于所有能执行撤销和重做的动作定义统一的接口:
public interface Undo { void execute(); void undo(); void redo(); }
2. UndoManager
要实现撤销(undo)和重做(redo),需要一个UndoManager来管理所有Undo和Redo,这个UndoManager是整个撤销和重做的核心,下面是具体的实现代码:
public class UndoManager { private static final UndoManager instance = new UndoManager(30); public static UndoManager getInstance() { return instance; } private final int maxSize; private final List<Undo> undoStack = new ArrayList<>(); private final List<Undo> redoStack = new ArrayList<>(); public UndoManager(int maxSize) { this.maxSize = maxSize; } public void push(Undo undo) { undo.execute(); undoStack.add(0, undo); if (undoStack.size() > maxSize) { undoStack.remove(undoStack.size() - 1); } redoStack.clear(); } public void clear() { undoStack.clear(); redoStack.clear(); } public boolean canUndo() { return !undoStack.isEmpty(); } public void undo() { final Undo undo = undoStack.get(0); undo.undo(); undoStack.remove(0); redoStack.add(0, undo); } public boolean canRedo() { return !redoStack.isEmpty(); } public void redo() { final Undo undo = redoStack.get(0); undo.redo(); redoStack.remove(0); undoStack.add(0, undo); } }
3. 使用UndoManager
UndoManager的使用非常简单,直接通过UndoManager.getInstance()获取实例即可,默认情况下支持撤销的次数为30。
下面是一个使用例子:
public class Sample02 extends Application { @Override public void start(Stage stage) { UndoManager undoManager = UndoManager.getInstance(); StackPane root = new StackPane(); VBox container = new VBox(); container.setSpacing(10); container.setAlignment(Pos.CENTER); root.getChildren().add(container); Button btn = new Button("新增文本"); AtomicInteger inc = new AtomicInteger(0); btn.setOnMouseClicked(event -> undoManager.push(new TextUndo(container, new Label("米虫2022" + inc.incrementAndGet())))); Button undo = new Button("撤销(undo)"); undo.setOnMouseClicked(event -> { if (undoManager.canUndo()) { undoManager.undo(); } }); Button redo = new Button("重做(redo)"); redo.setOnMouseClicked(event -> { if (undoManager.canRedo()) { undoManager.redo(); } }); container.getChildren().addAll(btn, undo, redo); stage.setTitle("撤销 + 重做"); stage.setWidth(320); stage.setHeight(480); stage.setScene(new Scene(root)); stage.getIcons().clear(); stage.getIcons().add(new Image("logo.jpg")); stage.show(); } }
效果如下:
4. 监听
可以进一步完善UndoManager对外暴露变更属性,外部通过监听变更,跟无法undo或者redo时禁用相应的功能。
调整代码实现如下:
Button undo = new Button("撤销(undo)"); undo.setDisable(true); undo.setOnMouseClicked(event -> { if (undoManager.canUndo()) { undoManager.undo(); } }); Button redo = new Button("重做(redo)"); redo.setDisable(true); redo.setOnMouseClicked(event -> { if (undoManager.canRedo()) { undoManager.redo(); } }); undoManager.revisionProperty().addListener((ob, ov, nv) -> { undo.setDisable(!undoManager.canUndo()); redo.setDisable(!undoManager.canRedo()); });
效果如下:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)