利用swing的undo包实现Undo/Redo功能

来源:互联网 发布:混沌战域国宝进阶数据 编辑:程序博客网 时间:2024/05/16 17:15

利用swingundo包实现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,通过调用UndoManagerundo/redo方法来实现Undo/Redo功能。

 

使用undo包很简单,主要操作步骤如下:

1、创建UndoManager实例;

2、创建各种实现UndoableEdit的具体操作类;

3、调用某种操作时,创建一个具体操作类的实例,加入UndoManager

4、在Undo/Redo时,直接调用UndoManagerundo/redo方法。

 

二、应用实例

下面就以一个比较常见的例子结合上述的操作步骤来进行说明。

现有一个产品列表界面,提供插入,删除,移动产品位置等操作,这些操作均要可以撤消和重做。如下是该界面的截图:

 

以上产品列表界面中,具有添加,删除,上移,下移四个操作按钮和Undo/Redo两个按钮,产品列表用一个JList实现。

 

第一步、创建UndoManager实例。

SamplePanel是我们的产品列表界面实现类,因此我们在SamplePanel类的初始化中加入:

...

private UndoManager undoManager=new UndoManager();

...

 

第二步、定义添加,删除,上移,下移的具体操作类。

AddEdit类负责添加操作;

DeleteEdit类负责删除操作;

UpDownEdit类负责上移和下移操作。

 

swingMVC体系中,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();

    }

}

 

下面来分别实现AddEditDeleteEditUpDownEdit类,它们均继承自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就非常简单了:

撤消按钮的事件处理器中,直接调用UndoManagerundo方法;

重做按钮的事件处理器中,直接调用UndoManagerredo方法。

 

三、代码

以上实例包含6java文件,分别是:

SampleFrame.java  示例的启动类

SamplePanel.java 产品列表界面类

ListEdit.java 列表操作抽象类

AddEdit.java 添加操作类

DeleteEdit.java 删除操作类

UpDownEdit.java 上移/下移操作类

 

不能贴附件,只好列出所有代码了:

sample/SampleFrame.java

/*

 * SampleFrame 主窗口

 * Created on 2007/03/16, EricXYY

 */

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 2007/03/16, EricXYY

 */

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操作,直接调用UndoManagerundo/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 2007/03/16, EricXYY

 */

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 2007/03/16, EricXYY

 */

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 2007/03/16, EricXYY

 */

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 2007/03/16, EricXYY

 */

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?"重做上移元素":"重做下移元素";

    }

}

 
原创粉丝点击