2012-07-17 8 views
10

Ich habe einen XML-Editor erstellt und bin in der letzten Phase fest: Hinzufügen von Rückgängig/Redo-Funktionalität.Wie implementiere ich ein einfaches Undo/Redo für Aktionen in Java?

Ich muss nur rückgängig machen/wiederholen, wenn Benutzer Elemente, Attribute oder Text zum JTree hinzufügen.

Ich bin noch ziemlich neu in dieser Schule, aber in der Schule habe ich (erfolglos) versucht, zwei stack object [] namens Rückgängig und Wiederholen zu erstellen und die durchgeführten Aktionen hinzuzufügen.

zum Beispiel, ich habe:

Action AddElement() { 

// some code 
public void actionPerformed(ActionEvent e) { 

        performElementAction(); 
       } 
} 

die performElementAction gerade fügt tatsächlich ein Element zum JTree.

Ich möchte einen Weg hinzufügen, diese Aktion hinzugefügt, um meine Rückgängig-Stack durchgeführt. Gibt es einen einfachen Weg, einfach rückgängig zu machen.push (die gesamte Aktion durchgeführt) oder etwas?

Leider wie ein Bösewicht für klingen, aber das ist, was ich bin :(

+0

Werfen Sie einen Blick auf die integrierte Rückgängig-Unterstützung; Ich habe es nie benutzt und ich kann kein Swing-Tutorial dafür finden, aber [hier] (http://docs.oracle.com/javase/6/docs/api/javax/swing/undo/UndoManager.html) ist der Manager. –

Antwort

7

Werfen Sie einen Blick auf die Command Pattern, umfassen seine Verwendungen rückgängig machen Implementierung/Redo-Funktionalität.

0

Ich würde versuchen, eine Aktion zu erstellen Klasse, mit einer AddElementAction-Klasse, die von Action erbt AddElementAction könnte eine Do() - und Undo() -Methode haben, die Elemente hinzufügen/entfernen würde Sie können dann zwei Stapel von Aktionen für Undo/Redo behalten und Do() aufrufen/Rückgängig() auf dem oberen Element, bevor es ausgegeben wird

+0

Siehe ['javax.swing.undo.UndoManager'] (http://docs.oracle.com/javase/6/docs/api/javax/swing/undo/UndoManager.html) –

1

Dieser tutorial erläutert die Grundzüge des Befehlsmusters und den Undo/Redo-Mechanismus für Swing. Ich hoffe es hilft.

+0

Dieser Artikel ist wirklich hilfreich. Vielen Dank. –

0

You have to define undo(), redo() operations along with execute() in Command interface itself.

Beispiel:

interface Command { 

    void execute() ; 

    void undo() ; 

    void redo() ; 
} 

definieren einen Staat in Ihrer ConcreteCommand Klasse. Abhängig vom aktuellen Status nach der execute() - Methode müssen Sie entscheiden, ob der Befehl zu Rückgängig-Stack oder Redo-Stack hinzugefügt werden soll und die Entscheidung entsprechend treffen.

Werfen Sie einen Blick auf diese undo-redo Befehl Artikel zum besseren Verständnis.

2

TL; DR: Sie können Aktionen zum Rückgängigmachen und Wiederherstellen unterstützen, indem Sie die Befehls- und Memento-Muster implementieren (Design Patterns - Gama et. al).

Das Memento Pattern

Dieses einfache Muster können Sie die Zustände eines Objekts speichern. Wickeln Sie das Objekt einfach in eine neue Klasse und aktualisieren Sie es, sobald sich sein Status ändert.

public class Memento 
{ 
    MyObject myObject; 

    public MyObject getState() 
    { 
     return myObject; 
    } 

    public void setState(MyObject myObject) 
    { 
     this.myObject = myObject; 
    } 
} 

Das Command-Muster

Das Befehlsmuster speichert das ursprüngliche Objekt und das memento Objekt (das wir Undo/Redo unterstützen möchten), die wir im Fall eines Undo benötigen.Darüber hinaus sind 2-Methoden definiert:

  1. auszuführen: führt den Befehl
  2. unExecute: entfernt den Befehl

Code:

public abstract class Command 
{ 
    MyObject myObject; 
    Memento memento; 

    public abstract void execute(); 

    public abstract void unExecute(); 
} 

Die definieren die logische " Aktionen, die den Befehl erweitern (zB Einfügen):

public class InsertCharacterCommand extends Command 
{ 
    //members.. 

    public InsertCharacterCommand() 
    { 
     //instantiate 
    } 

    @Override public void execute() 
    { 
     //create Memento before executing 
     //set new state 
    } 

    @Override public void unExecute() 
    { 
     this.myObject = memento.getState()l 
    } 
} 

Auftragen der Muster:

Dieser letzte Schritt definiert die Undo/Redo-Verhalten. Ihre Kernidee besteht darin, einen Stapel von Befehlen zu speichern, der als Verlaufsliste der Befehle fungiert. Um Redo zu unterstützen, können Sie einen sekundären Zeiger beibehalten, wenn ein Rückgängig-Befehl angewendet wird. Beachten Sie, dass jedes Mal, wenn ein neues Objekt eingefügt wird, alle Befehle nach ihrer aktuellen Position entfernt werden; dass durch die deleteElementsAfterPointer Methode erreicht hat folgende Bedeutung haben:

private int undoRedoPointer = -1; 
private Stack<Command> commandStack = new Stack<>(); 

private void insertCommand() 
{ 
    deleteElementsAfterPointer(undoRedoPointer); 
    Command command = 
      new InsertCharacterCommand(); 
    command.execute(); 
    commandStack.push(command); 
    undoRedoPointer++; 
} 

private void deleteElementsAfterPointer(int undoRedoPointer) 
{ 
    if(commandStack.size()<1)return; 
    for(int i = commandStack.size()-1; i > undoRedoPointer; i--) 
    { 
     commandStack.remove(i); 
    } 
} 

private void undo() 
{ 
    Command command = commandStack.get(undoRedoPointer); 
    command.unExecute(); 
    undoRedoPointer--; 
} 

private void redo() 
{ 
    if(undoRedoPointer == commandStack.size() - 1) 
     return; 
    undoRedoPointer++; 
    Command command = commandStack.get(undoRedoPointer); 
    command.execute(); 
} 

Fazit:

Was dieser Entwurf mächtig macht, ist die Tatsache, dass Sie so viele Befehle hinzufügen können, wie Sie möchten (durch die Command Klasse erweitern), zB , RemoveCommand, UpdateCommand und so weiter. Darüber hinaus gilt das gleiche Muster für jede Art von Objekt, so dass das Design wiederverwendbar und veränderbar über verschiedene Anwendungsfälle.

+0

Wie würden Sie die Anzahl der Undo/Redo-Schritte begrenzen? Es ist nicht möglich, einen Stapel zu sortieren. – Atlas2k

+0

@ Atlas2k können Sie einen Schwellenwert haben (z. B. "int threshold = 30") und dann alle "älteren" Befehle auf der Grundlage der aktuellen Stapelgröße entfernen. Wenn Sie zum Beispiel 'insertCommand()' aufrufen und Ihre 'commandStack.size()' größer als 30 ist, dann entfernen Sie das erste Element ('commandStack.remove (0)'). Die Grundidee ist, den Stack auf eine bestimmte Größe zu begrenzen. Ja, komplexere Anwendungsfälle, aber definitiv machbar :) –

+0

Ich folgte diesem, um meine maximale Stackgröße zu setzen: http://ntsblog.homedev.com.au/index.php/2010/05/06/c-stack- mit Höchstgrenze / – Atlas2k