利用swing的undo包实现Undo/Redo功能
一、概述
javax.swing.undo包提供了一系列接口和类,利用它可以方便的在程序中加入Undo/Redo功能。
先说说undo包引入的一个概念——Edit。它是与Command模式中的Command类似的一个概念。Command模式将操作的执行逻辑封装到一个个Command对象中,解耦了操作发起者和操作执行逻辑之间的耦合关系:操作发起者要进行一个操作,不用关心具体的执行逻辑,只需创建一个相应的Command实例,调用它的执行接口即可。而在swing中,与界面交互的各种操作,比如插入,删除等被称之为Edit,实际上就是Command。
下面是undo包的几个主要接口和类:
UndoableEdit 顾名思义,它表示一个可以被Undo/Redo的操作;
AbstractUndoableEdit 实现UndoableEdit的抽象类;
UndoManager Undo/Redo管理器,各种UndoableEdit实例通过addEdit方法加入UndoManager,通过调用UndoManager的undo/redo方法来实现Undo/Redo功能。
使用undo包很简单,主要操作步骤如下:
1、创建UndoManager实例;
2、创建各种实现UndoableEdit的具体操作类;
3、调用某种操作时,创建一个具体操作类的实例,加入UndoManager;
4、在Undo/Redo时,直接调用UndoManager的undo/redo方法。
二、应用实例
下面就以一个比较常见的例子结合上述的操作步骤来进行说明。
现有一个产品列表界面,提供插入,删除,移动产品位置等操作,这些操作均要可以撤消和重做。如下是该界面的截图:
以上产品列表界面中,具有添加,删除,上移,下移四个操作按钮和Undo/Redo两个按钮,产品列表用一个JList实现。
第一步、创建UndoManager实例。
SamplePanel是我们的产品列表界面实现类,因此我们在SamplePanel类的初始化中加入:
...
private UndoManager undoManager=new UndoManager();
...
第二步、定义添加,删除,上移,下移的具体操作类。
AddEdit类负责添加操作;
DeleteEdit类负责删除操作;
UpDownEdit类负责上移和下移操作。
在swing的MVC体系中,JList是一个View类,操作内部数据的能力来自于它的数据模型类ListModel。因此我们的每个具体操作类,如添加,删除等均通过直接操纵ListModel来达到目的。这是swing界面开发中的惯用做法。
为此,我们先定义一个抽象的ListEdit类,含有一个ListModel成员,供其他具体操作类继承。
public abstract class ListEdit extends AbstractUndoableEdit{
//列表的数据模型
protected DefaultListModel model=null;
//操作的具体执行逻辑,留待子类实现
public abstract void execute();
//在这里,redo操作只是简单的执行一次execute。子类如无特殊需求,就不用覆盖它。
public void redo() throws CannotRedoException {
execute();
}
}
下面来分别实现AddEdit,DeleteEdit,UpDownEdit类,它们均继承自ListEdit类。
我们在execute方法中实现操作逻辑,在undo方法中实现Undo的逻辑。redo方法在ListEdit中已经实现,不用管它了。
这里提一下如下两个方法:
getUndoPresentationName()和getRedoPresentationName()方法可以为Undo/Redo操作提供描述。比如,如果要在菜单中提供“撤消删除”,“重做删除”菜单项而不是简单的无所指的“撤消”,“重做”菜单项,可以通过这两个方法来获得。
一个需要注意的问题是,在实现执行逻辑时要保留现场数据,以供Undo时按图索骥恢复现场。
比如,要执行Delete操作,我们要记住删除的元素和所在位置这两个现场数据,undo方法据此来在原位置插入被删除的元素。如果没有这两个现场数据,undo就无从下手了。
下面是DeleteEdit类的实现:
public class DeleteEdit extends ListEdit{
//被删除的元素
private Object element;
//删除发生的位置
private int index;
public DeleteEdit(DefaultListModel model,int index) {
this.model=model;
this.index=index;
}
public void execute() {
element=model.getElementAt(index);
if(element!=null){
model.removeElementAt(index);
}
}
public void undo() throws CannotUndoException {
if(element!=null){
model.insertElementAt(element,index);
}
}
public String getUndoPresentationName() {
return "撤消删除元素";
}
public String getRedoPresentationName() {
return "重做删除元素";
}
}
其他操作类的实现原理基本类似,这里不再赘述。