在開發文字編輯器時,撤銷與重做是基本且重要的功能。Java Swing 透過 javax.swing.undo 套件提供完整的支援,本文提供一個可直接套用的 UndoWrapper 類別,幫助您快速為 JEditorPane 或其他文字元件加入無限次數的 Undo/Redo 功能。
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.JTextComponent;
import javax.swing.undo.*;
public class UndoWrapper implements UndoableEditListener {
private UndoManager undoManager;
private UndoAction undoAction;
private RedoAction redoAction;
private JEditorPane textComponent;
public UndoWrapper(JEditorPane aComponent) {
textComponent = aComponent;
undoManager = new UndoManager();
undoAction = new UndoAction();
redoAction = new RedoAction();
// 註冊編輯事件監聽器
textComponent.getDocument().addUndoableEditListener(this);
// 綁定快捷鍵:Ctrl+Z / Ctrl+Y
textComponent.getInputMap().put((KeyStroke) undoAction.getValue(Action.ACCELERATOR_KEY), "undo");
textComponent.getInputMap().put((KeyStroke) redoAction.getValue(Action.ACCELERATOR_KEY), "redo");
textComponent.getActionMap().put("undo", undoAction);
textComponent.getActionMap().put("redo", redoAction);
}
@Override
public void undoableEditHappened(UndoableEditEvent e) {
undoManager.addEdit(e.getEdit());
undoAction.updateUndoState();
redoAction.updateRedoState();
}
// 內部類別:Undo 動作
class UndoAction extends AbstractAction {
public UndoAction() {
super("Cannot undo");
setEnabled(false);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl Z"));
}
@Override
public void actionPerformed(ActionEvent e) {
try { undoManager.undo(); }
catch (CannotUndoException cue) { cue.printStackTrace(System.err); }
updateUndoState();
redoAction.updateRedoState();
}
void updateUndoState() {
if (undoManager.canUndo()) {
setEnabled(true);
putValue(Action.NAME, "Undo");
} else {
setEnabled(false);
putValue(Action.NAME, "Cannot undo");
}
}
}
// 內部類別:Redo 動作
class RedoAction extends AbstractAction {
public RedoAction() {
super("Cannot redo");
setEnabled(false);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl Y"));
}
@Override
public void actionPerformed(ActionEvent e) {
try { undoManager.redo(); }
catch (CannotRedoException cre) { cre.printStackTrace(System.err); }
updateRedoState();
undoAction.updateUndoState();
}
void updateRedoState() {
if (undoManager.canRedo()) {
setEnabled(true);
putValue(Action.NAME, "Redo");
} else {
setEnabled(false);
putValue(Action.NAME, "Cannot redo");
}
}
}
// 供外部選單/工具列呼叫
UndoAction getUndoAction() { return undoAction; }
RedoAction getRedoAction() { return redoAction; }
}
只需一行程式碼即可啟用功能:
JEditorPane editorPane = new JEditorPane();
new UndoWrapper(editorPane); // 完成!支援無限次 Undo/Redo
若需在選單或工具列加入按鈕,可呼叫 getUndoAction() 與 getRedoAction() 取得對應的 Action 物件。
CannotUndoException、CannotRedoException 避免程式異常中斷。