Undo框架(三)

21.5 使用外部对象管理Undo状态

在前面的示例中,我们自定义的UndoableEdit实现要负责维护可撤销对象的之前与之后状态。Swing Undo框架同时支持使用可撤销编辑实现之外的对象来管理状态的能力。当使用一个外部对象用于状态管理时,我们并不需要实现UndoableEdit接口。相反,我们可以使用StateEdit类作为UndoableEdit实现。然后StateEdit类依赖一个类实现StateEditable接口来管理可撤销对象(在Hashtable中)之前与之后的状态存储。


21.5.1 StateEditable接口

StateEditable接口由两个方法与一个无意义的字符串常量组成。

public interface StateEditable {
  public final static String RCSID;
  public void restoreState(Hashtable state);
  public void storeState(Hashtable state);
}

支持操作撤销的对象使用storeState(Hashtable)方法存储其状态。这是关于可以修改的对象的状态的全部信息。然后使用restoreState(Hashtable)方法重新载入对象的状态。

为了进行演示,我们来看一下如何重新实现前面的UndoableDrawingPanel示例。在更新的版本中使用这个接口,可撤销的绘制面板需要实现此接口,并且存储与获取前面图21-5中所示的多边形。这是因为多边形是我们关心撤销的唯一状态信息。源码显示在列表21-7中。

package swingstudy.ch21;
 
import java.awt.Polygon;
import java.util.Hashtable;
 
import javax.swing.JPanel;
import javax.swing.undo.StateEditable;
 
public class UndoableDrawingPanel2 extends JPanel implements StateEditable {
 
	private static String POLYGON_KEY = "Polygon";
 
	@Override
	public void storeState(Hashtable state) {
		// TODO Auto-generated method stub
		state.put(POLYGON_KEY, getPolygon());
	}
 
	@Override
	public void restoreState(Hashtable state) {
		// TODO Auto-generated method stub
		Polygon polygon = (Polygon)state.get(POLYGON_KEY);
		if(polygon != null) {
			setPolgyon(polygon);
		}
	}
 
}

restoreState()方法返回的Hashtable只包含可以修改的键/值对。也有可能Hashtable的get()方法会为我们使用put()方法显式放入散列表中的内容返回null。所以,如列表21-7所示,我们需要在由散列表获取状态信息之后添加一个if-null检测语句。


21.5.2 StateEdit类

在我们实现了StateEditable接口之后,我们可以使用StateEdit类作为UndoableEdit实现。其中前面的UndoableDrawingPanel示例创建了一个自定义的UndoableDrawingEdit,新类创建了一个StateEdit实例。

StateEdit构造函数接受一个我们将要修改的StateEditable对象与一个可选的表示名字。在创建StateEdit对象之后,修改StateEditable对象然后通知StateEdit来end() StateEditable对象的修改。当StateEdit对象被通知修改已经结束时,他会对比状态可编辑对象的之前与之后状态并且由散列表中移除没有变化的键/值对。然后我们可以通过由UndoableEditSupport类维护的一个列表将UndoabelEdit发送到UndoableEditListener对象的列表。

StateEdit stateEdit = new StateEdit(UndoableDrawingPanel2.this);
// Change state of UndoableDrawingPanel2
polygon.addPoint(mouseEvent.getX(), mouseEvent.getY());
// Done changing state
stateEdit.end();
undoableEditSupport.postEdit(stateEdit);

在编辑被发送之后,UndoManager管理UndoableEdit的StateEdit实例,类似于其他的可撤销编辑对象。然后UndoManager请求StateEdit对象来通知其StateEditable对象来载入其前一个状态。对于其他的UndoableEdit对象也是如此。所以,其他的代码不需要修改。


21.5.3 一个完整的StateEditable/StateEdit示例

改写的UndoableDrawingPanel示例显示在列表21-8中,与列表21-5的不同之处以粗体显示。这个版本使用我们刚刚所描述的StateEditable/StateEdit组合。前面的测试程序被包含在main()方法来保持示例的完整。除了修改了绘制面板的类名,测试并没有进行修改并且将会得到与我们在图21-5中所见到的相同的结果,假定多边形具有相同的点集合。

package swingstudy.ch21;
 
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Hashtable;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.StateEdit;
import javax.swing.undo.StateEditable;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEditSupport;
 
public class UndoableDrawingPanel2 extends JPanel implements StateEditable {
 
	private static String POLYGON_KEY = "Polygon";
	UndoableEditSupport undoableEditSupport = new UndoableEditSupport(this);
	Polygon polygon = new Polygon();
 
	public UndoableDrawingPanel2() {
		MouseListener mouseListener = new MouseAdapter() {
			public void mouseReleased(MouseEvent event) {
				StateEdit stateEdit = new StateEdit(UndoableDrawingPanel2.this);
				polygon.addPoint(event.getX(), event.getY());
				stateEdit.end();
				undoableEditSupport.postEdit(stateEdit);
				repaint();
			}
		};
		addMouseListener(mouseListener);
	}
 
	public void addUndoableEditListener(UndoableEditListener undoableEditListener) {
		undoableEditSupport.addUndoableEditListener(undoableEditListener);
	}
 
	public void removeUndoableEditListener(UndoableEditListener undoableEditListener) {
		undoableEditSupport.removeUndoableEditListener(undoableEditListener);
	}
 
	@Override
	public void storeState(Hashtable state) {
		// TODO Auto-generated method stub
		state.put(POLYGON_KEY, getPolygon());
	}
 
	@Override
	public void restoreState(Hashtable state) {
		// TODO Auto-generated method stub
		Polygon polygon = (Polygon)state.get(POLYGON_KEY);
		if(polygon != null) {
			setPolygon(polygon);
		}
	}
 
	public void setPolygon(Polygon newValue) {
		polygon = newValue;
		repaint();
	}
 
	public Polygon getPolygon() {
		Polygon returnValue;
		if(polygon.npoints == 0) {
			returnValue = new Polygon();
		}
		else {
			returnValue = new Polygon(polygon.xpoints, polygon.ypoints, polygon.npoints);
		}
		return returnValue;
	}
 
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		g.drawPolygon(polygon);
	}
 
	public static void main(String[] args) {
		Runnable runner = new Runnable() {
			public void run() {
				JFrame frame = new JFrame("Drawing Sample2");
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				UndoableDrawingPanel2 drawingPanel = new UndoableDrawingPanel2();
 
				UndoManager manager = new UndoManager();
				drawingPanel.addUndoableEditListener(manager);
 
				JToolBar toolbar = new JToolBar();
				JButton undoButton = new JButton(UndoManagerHelper.getUndoAction(manager));
				toolbar.add(undoButton);
				JButton redoButton = new JButton(UndoManagerHelper.getRedoAction(manager));
				toolbar.add(redoButton);
 
				frame.add(toolbar, BorderLayout.NORTH);
				frame.add(drawingPanel, BorderLayout.CENTER);
				frame.setSize(300, 150);
				frame.setVisible(true);
			}
		};
		EventQueue.invokeLater(runner);
	}
}


21.6 小结

在本章我们简单了解并且探讨了javax.swing.undo包中的Undo框架以及javax.swing.evnet的支持。我们了解框架在Swing文本组件中的支持。另外,我们了解了如何在我们自己的类中构建支持。通过使用Undo框架中的接口与类,我们可以使得任意可编辑的类同时支持撤销与重做功能。

下一章将会介绍Swing组件集合的可访问性支持。到时我们将会了解组件的辅助技术与声音支持。

在C++中实现撤销(Undo)功能,通常需要使用设计模式来记录对象状态的变更历史。一种常用的方法是命令模式(Command Pattern),在这种模式中,每个命令都是一个对象,这些对象封装了执行和撤销操作的方法。此外,可以使用Memento模式来保存对象的内部状态,以便在撤销操作时可以将对象恢复到某个特定状态。 以下是一个简化的Undo功能实现示例: 1. 定义命令接口,它包含执行(execute)和撤销(undo)操作的函数。 2. 实现具体命令类,这些类继承自命令接口,并在其中保存执行前和执行后的状态。 3. 使用一个命令历史记录来管理命令对象,这样可以按照执行顺序存储和检索命令对象。 4. 当需要撤销操作时,从命令历史记录中取出最后一个命令对象并调用其撤销方法。 下面是一个简单的代码框架,展示了如何实现一个可撤销的简单命令: ```cpp #include <iostream> #include <stack> #include <memory> // 命令接口 class Command { public: virtual ~Command() {} virtual void execute() = 0; virtual void undo() = 0; }; // 具体命令类 class ConcreteCommand : public Command { private: int m_stateBeforeExecute; int m_stateAfterExecute; public: ConcreteCommand() : m_stateBeforeExecute(0), m_stateAfterExecute(0) {} void execute() override { m_stateBeforeExecute = 0; // 假设是获取当前状态 // 执行具体操作... m_stateAfterExecute = 1; // 假设是执行后的状态 } void undo() override { // 恢复到执行前的状态 std::cout << "Undoing change to state: " << m_stateAfterExecute << std::endl; } }; // 命令历史记录 class CommandHistory { private: std::stack<std::unique_ptr<Command>> m_history; public: void push(std::unique_ptr<Command> command) { m_history.push(std::move(command)); } void undo() { if (!m_history.empty()) { m_history.top()->undo(); m_history.pop(); } else { std::cout << "No commands to undo." << std::endl; } } }; int main() { CommandHistory history; auto cmd1 = std::make_unique<ConcreteCommand>(); cmd1->execute(); history.push(std::move(cmd1)); // 执行其他操作... // 撤销操作 history.undo(); return 0; } ``` 在这个例子中,我们创建了一个`ConcreteCommand`类来执行具体的操作,并在执行前后保存状态。`CommandHistory`类用于保存命令对象的历史记录,并提供了一个`undo`方法来撤销最近的操作。在实际应用中,撤销操作可能需要保存更多的状态信息,以确保能够正确地恢复到之前的任意状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值