Undo架構(一)

來源:互聯網
上載者:User
在第20章中,我們通過探討可插撥的觀感體繫結構支援瞭解了如何自訂基於Swing的程式。在本章中,我們將會探討由作為Swing包一部分的JFC所提供的Undo架構。

Sun的Swing包包含一個支援我們程式中撤銷操作支援的公用程式。他允許我們支援修改我們資料狀態的撤銷與重做操作。儘管這個架構是Swing包階層的一部分,他卻可以用在任意的程式中,而不僅是基於組件的程式。


21.1 使用Undo架構

Undo架構位於javax.swing.undo包中,包含五個類,兩個介面以及兩個異常。要支援Undo架構,在javax.swing.event包中還有一個相關的癌與事件。在其頂部是UndoableEdit介面。這個介面構成了用於封裝使用Command設計模式可以撤銷或是重做的操作。

可撤銷命令的根實作類別是AbstractUndoableEdit類。不要叫這個類名迷惑我們,他並不是抽象的。根命令的子命令是CompundEdit與StateEdit命令類。

CompoundEdit類可以使得我們組合多個可撤銷的操作,其中一些可撤銷的操作則是儲存狀態變化的StateEdit對象。Swing文本組件會在他們的內容發生變化時建立DefaultDocumentEvent命令。這個命令是CompundEdit的子類,同時也是AbstractDocument的內聯類。另一個封裝的命令是UndoManager,他是CompundEdit的子類。

UndoManager通過承擔UndoableEditListener的角色並且響應UndoableEditEvent的建立來管理可編輯對象上的編輯操作。當UndoableEdit不可撤銷時,則會拋出CannotUndoException。另外,當一個UndoableEdit不可以重做時,則會拋出CannotRedoException。

如查我們希望建立支援撤銷與重做操作的對象,對象需要實現StateEditable介面,並且他們可以使用UndoableEditSupport類來協助管理UndoableEdit對象的列表。

在深入單個Undo架構的片段細節之前,我們先來瞭解一下如何在Swing文本組件中使用Undo架構。如果這些就是我們所需要的,我們就不需要理解其他的工作了。


21.2 配合Swing文本組件使用Undo架構

Swing文本組件已經支援了必須的撤銷與重做功能。我們並不需要使用UndoManager來進行管理並且通知管理器何時撤銷/重做。

作為一個樣本,考慮一個包含一個JTextArea與兩個用於撤銷與重做的工具列按鈕,21-1所示。

要使得圖21-1中的JTextArea支援撤銷操作,我們必須將一個UndoableEditListener關聯到組件的Document。我們需要做的就是將UndoManager作為我們的監聽器。首先,我們建立管理器,然後關聯。

UndoManager manager = new UndoManager();textArea.getDocument().addUndoableEditListener(manager);

一旦管理器被關聯到JTextArea的文檔,他就會監聽文本域內容的所有變化。因為每一個Swing文本組件都有一個Document資料模型,我們可以直接將UndoManager與這些組件的文檔相關聯。

在將管理器關聯到文本組件之後,我們必須提供一些方法來通知管理器撤銷或是重做某一操作。通常這是通過菜單選擇或是工具列按鈕選擇來實現的。在圖21-1中,這是藉助於JToolBar上的按鈕來實現的,而另一個按鈕用於實現重做。對於Undo按鈕,我們希望管理撤銷一個操作。所以,按鈕的ActionListener應該調用UndoManager的public void undo()方法。Redo按鈕的ActionListener應該調用管理器的public void redo()方法。undo()方法與redo()方法都會拋出必須處理的異常。

因為Undo與Redo按鈕所必須的功能對於所用的管理器都是相同的,我們將會在列表21-1中包含一個助手類UndoManagerHelper來為我們建立Action對象。這些對象可以為JMenuBar,JToolBar,或是其他可以使用ActionListener響應用於處理撤銷與重做操作的組件所用。我們必須向助手類請求Action,然後將Action關聯到相應的組件。例如,下面的五行代碼將會擷取前面所建立的UndoManager,並且為JToolBar添加必要的按鈕:

JToolBar toolbar = new JToolBar();JButton undoButton = new JButton(UndoManagerHelper.getUndoAction(manager));toolbar.add(undoButton);JButton redoButton = new JButton(UndoManagerHelper.getRedoAction(manager));toolbar.add(redoButton);

使用Swing文本組件的撤銷功能就是如此簡單。UndoManagerHelper類的定義顯示在列表21-1中。如果我們不喜歡預設的按鈕標籤(顯示在圖21-1中),還有一些支援自訂的其他方法。另外,如果在撤銷或是重做過程中拋出異常,則會彈出一個警告資訊。警告資訊與彈出容器標題也是可以自訂的。

package swingstudy.ch21; import java.awt.Component;import java.awt.event.ActionEvent; import javax.swing.AbstractAction;import javax.swing.Action;import javax.swing.JOptionPane;import javax.swing.UIManager;import javax.swing.undo.CannotRedoException;import javax.swing.undo.CannotUndoException;import javax.swing.undo.UndoManager; public class UndoManagerHelper { public static Action getUndoAction(UndoManager manager, String label) {return new UndoAction(manager, label);} public static Action getUndoAction(UndoManager manager) {return new UndoAction(manager, (String)UIManager.get("AbstractUndoableEdit.undoText"));} public static Action getRedoAction(UndoManager manager, String label) {return new RedoAction(manager, label);} public static Action getRedoAction(UndoManager manager) {return new RedoAction(manager, (String)UIManager.get("AbstractUndoableEdit.redoText"));} private abstract static class UndoRedoAction extends AbstractAction {UndoManager undoManager = new UndoManager();String errorMessage = "Cannot undo";String errorTitle = "Undo Problem"; protected UndoRedoAction(UndoManager manager, String name) {super(name);undoManager = manager;} public void setErrorMessage(String newValue) {errorMessage = newValue;} public void setErrorTitle(String newValue) {errorTitle = newValue;} protected void showMessage(Object source) {if (source instanceof Component) {JOptionPane.showMessageDialog((Component)source, errorMessage, errorTitle, JOptionPane.WARNING_MESSAGE);}else {System.err.println(errorMessage);}}} public static class UndoAction extends UndoRedoAction {public UndoAction(UndoManager manager, String name) {super(manager, name);setErrorMessage("Cannot undo");setErrorTitle("Undo Problem");} public void actionPerformed(ActionEvent event) {try {undoManager.undo();}catch(CannotUndoException cannotUndoException) {showMessage(event.getSource());}}} public static class RedoAction extends UndoRedoAction {public RedoAction(UndoManager manager, String name) {super(manager, name);setErrorMessage("Cannot redo");setErrorTitle("Redo Problem");} public void actionPerformed(ActionEvent event) {try {undoManager.redo();}catch(CannotRedoException cannotRedoException) {showMessage(event.getSource());}}}}

圖21-1中所示樣本的其餘原始碼顯示在列表21-2中。藉助於新建立的UndoManagerHelper類,在Swing文本組件中使用Undo架構的最大困難已經得到了極大的簡化。

package swingstudy.ch21; import java.awt.BorderLayout;import java.awt.EventQueue; import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JScrollPane;import javax.swing.JTextArea;import javax.swing.JToolBar;import javax.swing.undo.UndoManager; public class UndoSample { /** * @param args */public static void main(String[] args) {// TODO Auto-generated method stub Runnable runner = new Runnable() {public void run() {JFrame frame = new JFrame("Undo Sample");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);JTextArea textArea = new JTextArea();JScrollPane scrollPane = new JScrollPane(textArea); UndoManager manager = new UndoManager();textArea.getDocument().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(scrollPane, BorderLayout.CENTER);frame.setSize(300, 150);frame.setVisible(true);}};EventQueue.invokeLater(runner);} }

如果我們只是希望在Swing文本組件中使用Undo架構,我們可以跳過本章的其他部分。相反,如果我們希望在其他的組件中使用該架構,或者甚至是在非組件設定中使用,我們需要閱讀其餘部分,我們將會在餘下的內容中深入探討架構的內部實現。


21.3 Command設計模式

javax.swing.undo包的撤銷功能實現了Command設計模式,其包括如下組成部分:

  • Command:UndoableEdit介面定義了用於執行撤銷與重做操作的介面。
  • Concrete Command:AbstractUndoableEdit類的執行個體,或者是更為特定的子類,實現了必須的Command介面。他們將命令綁定到接收者(Document)來修改其內容。
  • Client:在Swing文本組件的例子中,Document執行了實際的AbstractUndoableEdit子類的建立,預設為AbstractDocument.DefaultDocumentEvent。
  • Invoker:UndoManager扮演UndoableEdit命令調用者的角色。通常情況下,其他的參與者會通知調用者何時執行調用。然而,調用者會通知特定的UndoableEdit執行個體何時撤銷或是重做命令。
  • Receiver:Document是實際的AbstractUndoabelEdit子類中命令的接收者。他知道如何執行請求。

如果我們要Swing文本組件之外使用Undo架構,Document元素將會為特定於我們客戶程式的接收者所替代。我們需要建立我們自己的UndoableEdit介面實現來承擔模式中Concrete Command的任務。無需直接實現介面,我們只需要繼承AbstractUndoableEdit類來封裝關於我們命令的特定資訊。

Command設計模式十分強大。無論我們在模式中使用哪種命令,我們都可以設定功能,例如用於執行自動化的測試的宏,因為調用會順序執行這些命令。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.