`
saga_java
  • 浏览: 14922 次
  • 性别: Icon_minigender_1
  • 来自: Georgia
社区版块
存档分类

java 实现undo/redo 三

阅读更多
三, UndoManager

要实现多次的Undo,Redo,必须要有一个数据结构管理多个UndoableCommand, 这个数据结构可以有多种选择, ArrayList, LinkedList, Stack等都可以。这里用下标访问元素的操作要多一些,所以采用ArrayList。另外,还要考虑多线程的情况,Java Swing 的UndoManager采用的是Vector, 但是有些情况下,操作只是在一个线程上工作,这时候用
ArrayList能获得更好的性能。这里的UndoManager提供了单线程和多线程两种选择。

    private List<UndoableCommand> commandList;

    private UndoManager(List<UndoableCommand> list) {
        commandList = list;
    }

    public static UndoManager getInstance() {
        return new UndoManager(new ArrayList<UndoableCommand>());
    }

    public static UndoManager getSynchronizedInstance() {
        List list = Collections.synchronizedList(new ArrayList<UndoableCommand>());
        return new UndoManager(list);
}

仔细考察commandList,可以发现只有在第一个或者最后一个元素时执行undo() 和redo()时是同一个元素外,其他情况下,执行undo() 和redo()功能是两个不同的但是相邻的元素。而且,第一个元素执行undo() 后整个commandList就不能undo()了,同理,最后一个元素执行redo() 后整个commandList就不能redo()了,其他情况下,commandList是既可以undo() 也可以redo()的。因此,这里采用一个变量 undoIndex 来记住执行undo() 的元素的位置。
undoIndex 的范围在-1(这时commandList不能undo())和最后一个元素下标(这时commandList不能redo())之间, redo() 的元素的位置应该是undoIndex + 1。


  private static final int CAN_NOT_UNDO_INDEX = -1;
  private static final int REDO_UNDO_INDEX_INTERVAL = 1;

    public boolean canUndo() {
        return undoIndex > CAN_NOT_UNDO_INDEX;
    }

    public boolean canRedo() {
        return undoIndex < getLastIndex();
    }

    private int getLastIndex() {
        return commandList.size() - 1;
    }

private int getRedoIndex() {
   return undoIndex + REDO_UNDO_INDEX_INTERVAL;
}

下面是完成UndoManager功能的3个主要function了:

manageCommand(UndoableCommand command) 做3件事
执行传来的UndoableCommand, 把它加到commandList的尾部,并把undoIndex指向这个尾部的位置

       public void manageCommand(UndoableCommand command) {
        command.execute();
        commandList.add(command);
        setUndoIndex(getLastIndex());
    }

undo(): 当commandList不能undo()时调用会抛出异常,如果当前undoIndex的元素可以undo(),则执行这个元素的undo(),undoIndex指向commandList上一个位置,这时还要找到下一次commandList undo() 元素的位置。主要要处理2种情况:可以一起执行的元素要依次执行,使得看上去像一起执行;不能执行undo()的元素要从commandList里删除,微软的WORD就是这样的,你在WORD里先输入一个A,执行撤销,这时再输入B,再执行撤销,你会发现已经不可能撤销到输入A这一步了。

public void undo() {
        if (!canUndo()) {
            throw new CannotUndoException();
        }
        UndoableCommand current = commandList.get(undoIndex);
        undoCommand(current);
        for (int i = undoIndex; i >= 0; i--) {
            UndoableCommand temp = commandList.get(i);
            if (!temp.canUndo()) {
                removeCommand(temp);
            } else if (current.canAppendWith(temp)) {
                undoCommand(temp);
                current = temp;
            } else {
                setUndoIndex(i);
                break;
            }
        }
}
redo(): 处理commandList 的 redo(), 跟undo()处理过程相反,只是相对简单一点。

下面是完整的UndoManager源码:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;

public class UndoManager {

    private static final int CAN_NOT_UNDO_INDEX = -1;
    private static final int REDO_UNDO_INDEX_INTERVAL = 1;
    private List<UndoableCommand> commandList;
    private int undoIndex;

    private UndoManager(List<UndoableCommand> list) {
        commandList = list;
    }

    public static UndoManager getInstance() {
        return new UndoManager(new ArrayList<UndoableCommand>());
    }

    public static UndoManager getSynchronizedInstance() {
        List list = Collections.synchronizedList(new ArrayList<UndoableCommand>());
        return new UndoManager(list);
    }

    public void manageCommand(UndoableCommand command) {
        command.execute();
        commandList.add(command);
        setUndoIndex(getLastIndex());
    }

    private void removeCommand(UndoableCommand command) {
        commandList.remove(command);
        undoIndex--;
    }

    private void undoCommand(UndoableCommand command) {
        command.undo();
        undoIndex--;
    }

    private void redoCommand(UndoableCommand command) {
        command.redo();
        undoIndex++;
    }

    public boolean canUndo() {
        return undoIndex > CAN_NOT_UNDO_INDEX;
    }

    public boolean canRedo() {
        return undoIndex < getLastIndex();
    }

    public void undo() {
        if (!canUndo()) {
            throw new CannotUndoException();
        }
        UndoableCommand current = commandList.get(undoIndex);
        undoCommand(current);
        for (int i = undoIndex; i >= 0; i--) {
            UndoableCommand temp = commandList.get(i);
            if (!temp.canUndo()) {
                removeCommand(temp);
            } else if (current.canAppendWith(temp)) {
                undoCommand(temp);
                current = temp;
            } else {
                setUndoIndex(i);
                break;
            }
        }
    }

    public void redo() {
        if (!canRedo()) {
            throw new CannotRedoException();
        }
        int redoIndex = getRedoIndex();
        UndoableCommand current = commandList.get(redoIndex);
        redoCommand(current);
        for (int i = getRedoIndex(); i < commandList.size(); i++) {
            UndoableCommand temp = commandList.get(i);
            if (temp.canAppendWith(current)) {
                redoCommand(current);
                current = temp;
            } else {
                return;
            }
        }
    }

    public void reset() {
        commandList.clear();
        setUndoIndex(CAN_NOT_UNDO_INDEX);
    }

    private void setUndoIndex(int index) {
        undoIndex = index;
    }

    private int getLastIndex() {
        return commandList.size() - 1;
    }

    private int getRedoIndex() {
        return undoIndex + REDO_UNDO_INDEX_INTERVAL;
    }
}

经测试,这个Undomanager类是可以工作的。

至此,这里提供的UndoableCommand抽象类和UndoManager类,已经封装了一些底层的Undo和Redo状态的切换,当实际项目中需要Undo/Redo功能时,只需要继承UndoableCommand实现自己的Undo/Redo逻辑并让UndoManager来manageCommand(UndoableCommand command),然后由UndoManage undo() 和 redo() 就可以了。

程序中Undo/Redo一般是菜单项或者是工具栏按钮,因此可以在UndoManager的基础上封装一个更为适用的UndoUtility类,它提供 getRedoAction() 和getUndoAction() function。
当使用时:
toolBar.add(undoUtility.getUndoAction());
toolBar.add(undoUtility.getRedoAction());
是不是很简单呢?
在本文的最后一部分,将给出这个类的代码,和本文提到的测试代码,这里先给出这个可无限次Undo/Redo的画,擦除直线的jar文件。
1
1
分享到:
评论

相关推荐

    Java 编辑器Undo Redo

    实现Java中编辑器的Undo、Redo操作,功能比较强大

    顺表网页电子表格(基于ExtJS插件) v2.0.rar

    是一款Javascript电子表格控件,顺表的源代码开放,可以用于Web报表设计器,Web电子表格编辑器,中国特色的电子表单设计器等,该Web Excel控件可以和Java/Jsp/Asp.Net集成, 实现更强大的电子表格在线编辑功能。...

    顺表网页电子表格 v2.0

    是一款Javascript电子表格控件,顺表的源代码开放,可以用于Web报表设计器,Web电子表格编辑器,中国特色的电子表单设计器等,该Web Excel控件可以和Java/Jsp/Asp.Net集成, 实现更强大的电子表格在线编辑功能。...

    设计模式Java-侯捷

    综上所述,本文主要围绕Java Swing框架下的Undoable功能展开,通过分析常见应用程序中Undo/Redo行为的特点,介绍了Java Swing提供的Undoable APIs以及如何使用它们来实现自己的撤销/重做功能。此外,还简要提及了...

    Editplus 3.5.1 可注册版 + 附注册机 + php 最新语法文件 + SQL 语法高清提示

    Other features include Hex Viewer, HTML toolbar, user tools, line number, ruler, URL highlighting, auto completion, cliptext, column selection, powerful search and replace, multiple undo/redo, spell ...

    EditPlus - text editor for Windows

    Other features include Hex Viewer, HTML toolbar, user tools, line number, ruler, URL highlighting, auto completion, cliptext, column selection, powerful search and replace, multiple undo/redo, spell ...

    JCreator Pro 2.5

    在功能上与Sun公司所公布的JDK等文字模式程序工具相较之下来得容易操作,还允许使用者自订义操作窗口界面及无限Undo /Redo等功能。 注册信息: Jcreator的版本:2.50.05 Name:javafans company:java.com.cn ...

    JCreator 2.50.05

    在功能上与Sun公司所公布的JDK等文字模式程序工具相较之下来得容易操作,还允许使用者自订义操作窗口界面及无限Undo /Redo等功能。 注册信息: Jcreator的版本:2.50.05 Name:javafans company:java.com.cn...

    patrones-mastermind-undo-redo-kiarras:patrones-mastermind-undo-redo-kiarras由GitHub Classroom创建

    Estées elcódigodel Mastermind con latécnicadel doble despacho,Sobre el que agregaremos los patrones y funcionalida necesaria para und Undo / Redo con undiseñoCorrecto。 先决条件再录取通知书...

    Ediplus_4.0

    Other features include HTML toolbar, user tools, line number, ruler, URL highlighting, auto completion, cliptext, column selection, powerful search and replace, multiple undo/redo, spell checker, ...

    备忘录模式笔记1

    * 它可以实现 Undo/Redo 操作,例如文本编辑器的撤销和重做操作。 * 它可以用于游戏存档、数据库rollback 等场景。 备忘录模式的缺点 虽然备忘录模式非常有用,但它也有一些缺点,例如: * 它可能会增加系统的...

    设计模式之命令模式Java实现

    在Java中实现命令模式,我们可以按照以下步骤进行: 1. **定义接口**: 首先,我们需要创建一个命令接口,这个接口定义了所有命令都需要执行的操作。例如,可以定义一个名为`Command`的接口,其中包含一个`execute...

    editplus.exe

    拥有无限制的Undo/Redo、英文拼字检查、自动换行、列数标记、搜寻取代、同时编辑多文件、全屏幕浏览功能。而它还有一个好用的功能,就是它有监视剪贴簿的功能,能够同步于剪贴簿自动将文字贴进EditPlus的编辑窗口中...

    EditPlus3_ha

    拥有无限制的Undo/Redo、英文拼字检查、自动换行、列数标记、搜寻取代、同时编辑多文件、全屏幕浏览功能。而它还有一个好用的功能,就是它有监视剪贴簿的功能,能够同步于剪贴簿自动将文字贴进EditPlus的编辑窗口中...

    java如何在绘图板上实现undo&#40;撤销操作&#41;.docx

    在Java编程中,实现撤销(Undo)操作是增强应用程序用户体验的重要功能,特别是在涉及用户交互和图形编辑的应用中。Swing库提供了一套完整的框架来帮助开发者轻松实现这一特性。主要涉及的类和接口位于`javax.swing....

    undo-redo-stack:撤消重做堆栈

    在实际编码过程中,撤消重做堆栈的实现可以采用多种语言,如C++、Java、Python或JavaScript。在面向对象语言中,通常会定义一个接口或抽象类来规定所有可撤销操作的共同行为,然后让具体操作类继承或实现这个接口。...

    java-Editor.zip_java editor_括号匹配 的JAVA 实现

    这一功能通常基于undo/redo堆栈,每次编辑操作都会在堆栈上留下一个状态,用户可以通过撤销和重做命令在这些状态之间切换。 最后,文本查找功能让开发者能够快速搜索代码中的特定字符串或模式。这个功能通常支持...

    最新EditPlus汉化破解64位32位.rar

    拥有无限制的 Undo/Redo、英文拼字检查、自动换行、列数标记、搜索替代、同时编辑多文件、全屏幕浏览等更加完善的功能,程序员可以把EditPlus当做(X)HTML、php、C/C++、Java、Perl等程序语言的简单的IDE来使用。...

    java 实现链栈存储的方法

    最后,我们可以使用链栈来实现一些实际的应用,例如解析语法树、实现 undo/redo 功能等。 ```java public class Demo { public static void main(String[] args) { LinkStack&lt;Integer&gt; ls = new LinkStack(); ...

Global site tag (gtag.js) - Google Analytics