专栏首页JavaEdgeSwing 的 undo 包实现撤销/重做功能

Swing 的 undo 包实现撤销/重做功能

0 Edit

与Command模式中的Command类似的一个概念。Command模式将操作的执行逻辑封装到一个个Command对象中,解耦了操作发起者和操作执行逻辑之间的耦合关系:操作发起者要进行一个操作,不用关心具体的执行逻辑,只需创建一个相应的Command实例,调用它的执行接口即可。 而在swing中,与界面交互的各种操作,比如插入,删除等被称之为Edit,实际上就是Command。

1 undo包

1.1 UndoableEdit接口

一个已完成的编辑操作(edit),一个可以被Undo/Redo的操作。

1.2 AbstractUndoableEdit

实现UndoableEdit。

1.3 CompoundEdit

  • 如果此编辑从未收到end
  • UndoableEdit撤消/恢复的集合,集体通过这一CompoundEdit
  • undo 发送undo所有包含UndoableEdits在它们被添加的顺序相反

1.4 UndoManager

负责实现 redo/undo。

各种UndoableEdit实例通过

addEdit

加入UndoManager

public synchronized boolean addEdit(UndoableEdit anEdit) {
     boolean retVal;

     // 从indexOfNextAdd到末尾进行修剪去尾,因为一旦新的操作被添加,我们永远无法访问到这些编辑
     trimEdits(indexOfNextAdd, edits.size()-1);

     retVal = super.addEdit(anEdit);
     if (inProgress) {
       retVal = true;
     }

     // 也许父类添加了此编辑,但也可能没有添加(也许正在进行的复合编辑(compound edit)代替了它。或者也许此UndoManager不再进行了)。因此,请确保indexOfNextAdd指向正确的位置。
     indexOfNextAdd = edits.size();

     // 加强限制
     trimForLimit();

     return retVal;
    }

通过调用UndoManager的

undo

撤消适当的编辑。如果已调用end,则此方法将调用父类方法,否则将调用undo对下一个编辑的索引和上一个显著编辑之间的所有编辑,适当地更新下一个编辑的索引

redo

方法来实现Undo/Redo功能。 这里提一下如下两个方法:

  • getUndoPresentationName() 返回此编辑可撤消形式的描述。 如果end已调用此电话为超。 否则,如果有编辑撤消,这将返回从下一个显著编辑将被撤销的价值。 如果没有编辑可以撤销, end并没有被调用这个返回从值UIManager财产“AbstractUndoableEdit.undoText”

  • getRedoPresentationName() 返回此编辑可重复执行形式的说明。 如果end已调用此电话为超。 否则,如果有编辑重做,这种回报从下一个显著的编辑将要恢复的价值。 如果没有编辑重做和end尚未援引这一收益来自值UIManager财产“AbstractUndoableEdit.redoText”

可以为Undo/Redo操作提供描述。比如,如果要在菜单中提供“撤消删除”,“重做删除”菜单项而不是简单的无所指的“撤消”,“重做”菜单项,可以通过这两个方法来获得。

2 使用undo包

1、创建UndoManager实例; 2、创建各种实现UndoableEdit的具体操作类; 3、调用某种操作时,创建一个具体操作类的实例,加入UndoManager; 4、在Undo/Redo时,直接调用UndoManager的undo/redo方法。

3 实例

产品列表用一个JList实现

3.1 创建UndoManager实例

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

3.2 创建各种实现UndoableEdit的具体操作类

定义添加,删除,上移,下移的具体操作类。 AddEdit类负责添加操作; DeleteEdit类负责删除操作; UpDownEdit类负责上移和下移操作。

ListEdit抽象类

含有一个ListModel成员,供其他具体操作类继承。

下面分别实现AddEdit,DeleteEdit,UpDownEdit类,它们均继承自ListEdit类。

在execute方法中实现操作逻辑,在undo方法中实现Undo的逻辑。redo方法在ListEdit中已经实现 一个需要注意的问题是,在实现执行逻辑时要保留现场数据,以供Undo时恢复现场。 比如,要执行Delete操作,我们要记住删除的元素和所在位置这两个现场数据,undo方法据此来在原位置插入被删除的元素。如果没有这两个现场数据,undo就无从下手了。

DeleteEdit类

其他操作类的实现原理基本类似,这里不再赘述。

package com.javaedge;

import javax.swing.DefaultListModel;
import javax.swing.undo.CannotUndoException;
/*
 * 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 "重做删除元素";
    }
}

3 在界面中调用添加,删除,上移,下移操作

以添加操作为例,在“添加”按钮的事件处理器中: 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类中的相应代码,不再一一列出。

4 调用Undo/Redo

在“撤消”按钮的事件处理器中,直接调用UndoManager的undo方法; 在“重做”按钮的事件处理器中,直接调用UndoManager的redo方法。

SampleFrame.java 示例的启动类 SamplePanel.java 产品列表界面类 ListEdit.java 列表操作抽象类 AddEdit.java 添加操作类 DeleteEdit.java 删除操作类 UpDownEdit.java 上移/下移操作类

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 处理器调度一、CPU调度的相关概念三、批处理系统中常用的调度算法四、交互式系统的调度算法五、多级反馈队列调度算法(重点)七、多处理器调度算法设计

    JavaEdge
  • Java计算机IT编程文档常见单词翻译

    | 英文 | 译法 1 | 译法 2 | 译法 3 | | query | 查询 | | | | query language | 查询语言 | | | ...

    JavaEdge
  • Java编程思想第五版精粹(五)-初始化和清理(下)

    以编译时错误的方式呈现。编译器可以为 i 赋一个默认值,但是未初始化的局部变量更可能是程序员的疏忽,所以强制程序员提供一个初始值,往往能帮助找出程序里的 bug...

    JavaEdge
  • 各种开源汇编、反汇编引擎的非专业比较

    由于平时业余兴趣和工作需要,研究过并使用过时下流行的各种开源的x86/64汇编和反汇编引擎。如果要对汇编指令进行分析和操作,要么自己研究Intel指令集写一个,...

    战神伽罗
  • 苹果OS X Yosemite系统曝多个本地提权漏洞

    国外安全研究人员近日曝光最新版Mac OSX 10.10.1系统上存在多处本地提权漏洞,由于提交到苹果官方时间太久都过未得到明确答复,导致研究者直接公布漏洞细节...

    FB客服
  • 彻底搞懂 etcd 系列文章(八):etcd 事务 API

    etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管。etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key...

    aoho求索
  • 小朋友学C语言(28):指针

    (一)内存地址 #include <stdio.h> int main() { int var1 = 20; printf("变量var1的值为...

    海天一树
  • ROS Tranform出错简易处理 tf出错现象及解析

    有时候由于urdf以及代码中有些遗漏,可能导致TF出现问题,此文只作为阐明TF重要性的示例,不是解决方案!

    zhangrelay
  • Android学习--RecyclerView

           前面一篇总结了ListView,在这篇我们总结一些这个RecyclerView,我们就从最基本的开始,安卓团队是将RecyclerView定义在s...

    Mr.RisingSun
  • 【互联网+公装】—齿轮易创合作伙伴inDeco官网上线,流量增长50~60%

    据统计,2016年的公装市场规模达到了2.38万亿元,且一直处于增长的态势。由于损耗较大,公装产品更新需求极快,一般历经5-10年就需要迭代,在“个性化”空间展...

    齿轮易创说互联网

扫码关注云+社区

领取腾讯云代金券