利用swing的undo包实现Undo/Redo功能
来源:互联网 发布:混沌战域国宝进阶数据 编辑:程序博客网 时间:2024/05/16 17:15
利用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 "重做删除元素";
}
}
其他操作类的实现原理基本类似,这里不再赘述。
第三步、在界面中调用添加,删除,上移,下移操作
以添加操作为例,在“添加”按钮的事件处理器中:
1、 准备好AddEdit所需的参数(这里除了ListModel外,还需要一个元素名称,通过弹出输入框来获取);
2、 创建AddEdit实例,调用其execute方法;
3、 将AddEdit实例加入UndoManager。
addButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String s=JOptionPane.showInputDialog(null,"请输入条目:","添加",JOptionPane.PLAIN_MESSAGE);
if(s!=null&&!s.equals("")){
AddEdit edit=new AddEdit(model,s);
edit.execute();
undoManager.addEdit(edit);
}
}
});
其他调用操作见SamplePanel类中的相应代码,不再一一列出。
第四步、调用Undo/Redo
经过上面的步骤,现在要实现Undo/Redo就非常简单了:
在“撤消”按钮的事件处理器中,直接调用UndoManager的undo方法;
在“重做”按钮的事件处理器中,直接调用UndoManager的redo方法。
三、代码
以上实例包含6个java文件,分别是:
SampleFrame.java 示例的启动类
SamplePanel.java 产品列表界面类
ListEdit.java 列表操作抽象类
AddEdit.java 添加操作类
DeleteEdit.java 删除操作类
UpDownEdit.java 上移/下移操作类
不能贴附件,只好列出所有代码了:
sample/SampleFrame.java
/*
* SampleFrame 主窗口
* Created on
*/
package sample;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class SampleFrame extends JFrame{
private SamplePanel sp=new SamplePanel();
public SampleFrame() {
setTitle("示例");
setSize(640,480);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null); //使窗口居中
getContentPane().add(sp,BorderLayout.CENTER);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new SampleFrame().setVisible(true);
}
});
}
}
sample/SamplePanel.java
/*
* SamplePanel 列表操作面板
* Created on
*/
package sample;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import sample.edit.AddEdit;
import sample.edit.DeleteEdit;
import sample.edit.UpDownEdit;
public class SamplePanel extends JPanel{
private JToolBar toolBar=new JToolBar();
private JButton addButton=new JButton("添加");
private JButton deleteButton=new JButton("删除");
private JButton upButton=new JButton("上移");
private JButton downButton=new JButton("下移");
private JButton undoButton=new JButton("撤消");
private JButton redoButton=new JButton("重做");
private JList list=new JList();
private JScrollPane scrollPane=new JScrollPane(list);
//列表的数据模型,各种操作均是针对model来进行
private DefaultListModel model=new DefaultListModel();
private UndoManager undoManager=new UndoManager();
public SamplePanel() {
setLayout(new BorderLayout());
toolBar.add(addButton);
toolBar.add(deleteButton);
toolBar.add(upButton);
toolBar.add(downButton);
toolBar.add(undoButton);
toolBar.add(redoButton);
toolBar.setFloatable(false);
add(toolBar,BorderLayout.NORTH);
list.setModel(model);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
add(scrollPane,BorderLayout.CENTER);
initSampleData();
initActions();
}
private void initActions(){
//对于每个操作,准备好Eidt所需的参数,创建Edit实例,执行之,然后加入UndoManager.
addButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String s=JOptionPane.showInputDialog(null,"请输入条目:","添加",JOptionPane.PLAIN_MESSAGE);
if(s!=null&&!s.equals("")){
AddEdit edit=new AddEdit(model,s);
edit.execute();
undoManager.addEdit(edit);
}
}
});
deleteButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
int index=list.getSelectedIndex();
if(index>-1){
DeleteEdit edit=new DeleteEdit(model,index);
edit.execute();
undoManager.addEdit(edit);
}
}
});
upButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
int index=list.getSelectedIndex();
if(index>0){
UpDownEdit edit=new UpDownEdit(model,index,true);
edit.execute();
undoManager.addEdit(edit);
}
}
});
downButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
int index=list.getSelectedIndex();
if((index>-1)&&(index<model.getSize()-1)){
UpDownEdit edit=new UpDownEdit(model,index,false);
edit.execute();
undoManager.addEdit(edit);
}
}
});
//对于Undo/Redo操作,直接调用UndoManager的undo/redo方法
undoButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
try{
undoManager.undo();
}
catch(CannotUndoException ex){}
}
});
redoButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
try{
undoManager.redo();
}
catch(CannotRedoException ex){}
}
});
}
private void initSampleData(){
model.addElement("笔记本");
model.addElement("台式机");
model.addElement("数码相机");
model.addElement("移动硬盘");
}
}
sample/edit/ListEdit.java
/*
* ListEdit 在列表上进行的操作的抽象类
* Created on
*/
package sample.edit;
import javax.swing.DefaultListModel;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
public abstract class ListEdit extends AbstractUndoableEdit{
//列表的数据模型
protected DefaultListModel model=null;
//操作的具体执行逻辑,留待子类实现
public abstract void execute();
//在这里,redo操作只是简单的执行一次execute,子类如无特殊需求,就不用覆盖它了。
public void redo() throws CannotRedoException {
execute();
}
}
sample/edit/AddEdit.java
/*
* AddEdit 添加操作
* Created on
*/
package sample.edit;
import javax.swing.DefaultListModel;
import javax.swing.undo.CannotUndoException;
public class AddEdit extends ListEdit{
//添加的元素
private Object element;
//添加的元素所在位置
private int index;
public AddEdit(DefaultListModel model,Object element) {
this.model=model;
this.element=element;
}
public void execute(){
model.addElement(element);
index=model.getSize()-1;
}
public void undo() throws CannotUndoException {
model.remove(index);
}
public String getUndoPresentationName() {
return "撤消添加元素";
}
public String getRedoPresentationName() {
return "重做添加元素";
}
}
sample/edit/DeleteEdit.java
/*
* DeleteEdit 删除操作
* Created on
*/
package sample.edit;
import javax.swing.DefaultListModel;
import javax.swing.undo.CannotUndoException;
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 "重做删除元素";
}
}
sample/edit/upDownEdit.java
/*
* UpDownEdit 上移下移操作
* Created on
*/
package sample.edit;
import javax.swing.DefaultListModel;
import javax.swing.undo.CannotUndoException;
public class UpDownEdit extends ListEdit{
//是否上移
private boolean up;
//待移动的原始位置
private int index;
//待移动的原始位置上的元素
private Object element1;
//目标位置上的元素
private Object element2;
public UpDownEdit(DefaultListModel model,int index,boolean up) {
this.model=model;
this.index=index;
this.up=up;
}
public void execute() {
element1=model.getElementAt(index);
if(up){
element2=model.getElementAt(index-1);
model.setElementAt(element1,index-1);
model.setElementAt(element2,index);
}
else{
element2=model.getElementAt(index+1);
model.setElementAt(element1,index+1);
model.setElementAt(element2,index);
}
}
public void undo() throws CannotUndoException {
if(up){
model.setElementAt(element1,index);
model.setElementAt(element2,index-1);
}
else{
model.setElementAt(element1,index);
model.setElementAt(element2,index+1);
}
}
public String getUndoPresentationName() {
return up?"撤消上移元素":"撤消下移元素";
}
public String getRedoPresentationName() {
return up?"重做上移元素":"重做下移元素";
}
}
- 利用swing的undo包实现Undo/Redo功能
- 利用Command模式实现无限次数的Undo/Redo功能
- 设计模式:利用Command模式实现无限次数的Undo/Redo功能
- C#实现的简易含undo/redo功能的winForm
- 实现编辑器的Undo Redo功能用Java来
- 实现编辑器的Undo Redo功能用Java来
- 使用Java来实现编辑器的Undo Redo功能
- UNDO REDO实现
- Undo/Redo实现
- Undo/Redo框架实现
- Undo/Redo框架的一种实现
- 文字录入无限制Undo,Redo的实现
- Undo/Redo的C#实现方式(原创)
- 多步Undo/Redo的实现
- Redo Undo 的三种实现
- 三种undo/Redo的实现
- 如何使用Swing的undo包
- redo & undo
- 下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?
- 多文档应用程序之同一个文档同一个框架内的试图切换
- oracle10g for linux 安装
- 微软动画指针漏洞(ANI漏洞,艾妮)
- 网站模仿
- 利用swing的undo包实现Undo/Redo功能
- XHTML和样式表
- css手册(二) Text 文字
- 平台软件列强争雄
- 弹出网页窗口
- 编译错误: pointer to void not legal here
- 数据库人员手边系列:SQL Server常见连接错误
- (第四版中文版)[三]Windows 的一些消息
- 拒绝session丢失 利用DIV层实现对模态窗口的模拟