前些天学习<<JAVA与模式>>,到命令模式时,随带给了一个CTRL+Z案例的实现,想来学习编程这么久,CTRL+Z还没有认真实现过。
因此,借助JAVA与模式里面的源代码,自己对撤销和回退进行了实现(JAVA与模式书中代码有部分问题)。此次用到了命令模式,因为有界面,有按钮,有接收者,有发送者。
以下是类图,只为方便,未考虑UML细节规范。
以下是程序的实现。
MainFrame类:提供Main方法,Client类:类似于看电视的人,这里关联了UndoableTextArea,ItsukyuQuatation是因为要初始化该对象
package undoRedo; import java.awt.Frame; import java.awt.event.*; public class MainFrame extends Frame { private static UndoableTextArea text; private static ItsukyuQuatation panel; public MainFrame(String title) { super(title); } public static void main(String[] args) { //构造可撤消的textArea text = new UndoableTextArea("Your text here"); //构造panel panel = new ItsukyuQuatation(text); //构造frame MainFrame frame = new MainFrame("测试undo和redo"); //增加窗体关闭事件 frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); frame.add(panel); frame.setSize(300,300); frame.setVisible(true); } }
ItsukyuQuatation类:面板类,里面包括undo和redo两个按钮,类似于命令模式中的摇控器。实现了ActionListener接口。
package undoRedo; import java.awt.BorderLayout; import java.awt.Panel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class ItsukyuQuatation extends Panel implements ActionListener { private final Command undo; private final Command redo; public ItsukyuQuatation(UndoableTextArea text) { this.setLayout(new BorderLayout()); Panel toolbar = new Panel(); undo = new UndoCommand(text); redo = new RedoCommand(text); undo.addActionListener(this); redo.addActionListener(this); toolbar.add(undo); toolbar.add(redo); this.add(toolbar,"North"); this.add(text,"Center"); } public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub Command cmd = (Command)e.getSource(); cmd.execute(); } }
UndoableTextArea类:类似于命令模式中的电视机类。实现了StateEditable接口,因此该类具有可撤销和重做的功能。重载的storeState和restoreState即是把文本中的内容放到hashTable里去。
该类是最核心的类,实现ctrl+z的原理简单说就是UndoManger.edits是一个Vector动态数组,对每次的按键动作,都会存储一份当前的文本内容到数据。当点击undo按钮,
数组索引(UnoManager类的indexOfNextAdd变量)前移一位,取得前一状态的文本内容。点击redo按钮,数组索引后移一位,取后一状态的文本内容。但是各状态内容均未删或修改。
initUndoable()方法,如果用keyPressed事件,那么最近的状态未加到undoManager.edits列里。因此选用keyReleased事件。如果监听valueChanged事件,那么代码就
和JAVA与模式一书中一样,只能实现最近一次的编辑撤消。
package undoRedo; import java.awt.TextArea; import java.awt.event.*; import java.util.Hashtable; import javax.swing.undo.*; import javax.swing.undo.StateEditable; public class UndoableTextArea extends TextArea implements StateEditable { private UndoManager undoManager; private final static String KEY_STATE="UndoableTextAreaKey"; private StateEdit currentEdit; boolean textChanged = false; public UndoableTextArea() { super(); initUndoable(); } public UndoableTextArea(String string) { super(string); initUndoable(); } //存储状态 public void storeState(Hashtable<Object, Object> hashTable) { // TODO Auto-generated method stub hashTable.put(KEY_STATE, this.getText()); } //还原状态 public void restoreState(Hashtable<?, ?> hashTable) { // TODO Auto-generated method stub Object data = hashTable.get(KEY_STATE); if(data!=null) { this.setText((String)data); } } //撤销方法 public boolean undo() { try { undoManager.undo(); System.out.println("undo=" + undoManager.toString()); return true; }catch(CannotUndoException e) { System.out.println("Can‘t undo"); } return false; } //重做方法 public boolean redo() { try { undoManager.redo(); System.out.println("redo=" + undoManager.toString()); return true; }catch(CannotRedoException e) { System.out.println("Can‘t redo"); } return false; } private void initUndoable() { this.undoManager = new UndoManager(); this.currentEdit = new StateEdit(this); this.addKeyListener(new KeyAdapter() { /* * * 如果用keyPressed事件,那么最近的状态未加到undoManager.edits列里。 * 因此选用keyReleased事件。 * public void keyPressed(KeyEvent event) { if(event.isActionKey()) { //takeSnapshot(); }else { textChanged = true; takeSnapshot(); } } */ public void keyReleased(KeyEvent event) { if(event.isActionKey()) { //takeSnapshot(); }else { textChanged = true; takeSnapshot(); } } }); this.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent event) { //takeSnapshot(); } }); } private void takeSnapshot() { if(textChanged) { this.currentEdit.end(); this.undoManager.addEdit(this.currentEdit); this.currentEdit = new StateEdit(this); System.out.println("takeSnapshot=" + undoManager.toString()); textChanged = false; } } }
Command抽象类:定义按钮,抽象方法 execute()
UndoCommand,RedoCommand均是Command类的实现,类似于命令模式中的具体按钮,调用接收者的方法进行实现。
package undoRedo; import java.awt.Button; public abstract class Command extends Button { public Command(String caption) { super(caption); } public abstract void execute(); }
package undoRedo; public class UndoCommand extends Command { UndoableTextArea text; public UndoCommand(UndoableTextArea text) { super("Undo"); this.text = text; } @Override public void execute() { // TODO Auto-generated method stub text.undo(); } }
package undoRedo; public class RedoCommand extends Command { UndoableTextArea text; public RedoCommand(UndoableTextArea text) { super("Redo"); this.text = text; } @Override public void execute() { // TODO Auto-generated method stub text.redo(); } }