ctrl+z的JAVA实现,借助了命令模式(command pattern)

前些天学习<<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();
    }

}

 

 

 

 

posted @ 2015-03-02 22:45  秀才坤坤  阅读(1135)  评论(0编辑  收藏  举报