2012-07-17 307 views
10

He creado un editor XML y estoy atascado en la última fase: agregar funcionalidad deshacer/rehacer.¿Cómo implemento un simple deshacer/rehacer para las acciones en java?

Solo tengo que agregar deshacer/rehacer para cuando los usuarios agreguen elementos, atributos o texto al JTree.

Todavía soy bastante nuevo en esto, pero en la escuela de hoy intenté (sin éxito) crear dos objetos de pila [] llamados deshacer y rehacer, y agregar las acciones realizadas en ellos.

por ejemplo, que tienen:

Action AddElement() { 

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

        performElementAction(); 
       } 
} 

la performElementAction acaba realmente añade un elemento a la JTree.

Quiero agregar una forma de agregar esta acción realizada a mi pila de deshacer. ¿Hay alguna manera sencilla de deshacer un.push (toda la acción realizada) o algo así?

Lo siento por sonar como un malo, pero eso es lo que soy :(

+0

Asegúrese de echar un vistazo al soporte de deshacer integrado; Nunca lo he usado y no puedo encontrar un tutorial de Swing para él, pero [aquí] (http://docs.oracle.com/javase/6/docs/api/javax/swing/undo/UndoManager.html) es el gerente. –

Respuesta

7

Tome un vistazo a la Command Pattern, sus usos incluyen la implementación de la funcionalidad de deshacer/rehacer.

0

me gustaría tratar de crear una acción class, con una clase AddElementAction heredando Acción. AddElementAction podría tener un método Do() y Deshacer() que agregaría/eliminaría elementos en consecuencia. Luego puede mantener dos pilas de Acciones para deshacer/rehacer, y simplemente llamar a Do()/Deshacer() en el elemento superior antes de abrirlo.

+0

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

1

Este tutorial explica los aspectos fundamentales del patrón de comando y el mecanismo de deshacer/rehacer para Swing. Espero eso ayude.

+0

Ese artículo es realmente útil. Gracias. –

0

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

ejemplo:

interface Command { 

    void execute() ; 

    void undo() ; 

    void redo() ; 
} 

definir un Estado en su clase ConcreteCommand. Dependiendo del estado actual después del método execute(), debe decidir si se debe agregar un comando a Undo Stack o Redo Stack y tomar las decisiones correspondientes.

Eche un vistazo a este artículo del comando undo-redo para una mejor comprensión.

2

TL; DR: Puede admitir acciones de deshacer y rehacer implementando los patrones Command y Memento (Design Patterns - Gama et. al).

El patrón Memento

Este patrón simple le permite guardar los estados de un objeto. Simplemente envuelva el objeto en una nueva clase y cada vez que cambie su estado, actualícelo.

public class Memento 
{ 
    MyObject myObject; 

    public MyObject getState() 
    { 
     return myObject; 
    } 

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

El patrón Comando

El patrón de comando almacena el objeto original (que queremos apoyar deshacer/rehacer) y el objeto de recuerdo, lo que necesitamos en caso de deshacer.Por otra parte, se definen 2 métodos:

  1. ejecutar: ejecuta el comando
  2. unExecute: elimina el comando

Código:

public abstract class Command 
{ 
    MyObject myObject; 
    Memento memento; 

    public abstract void execute(); 

    public abstract void unExecute(); 
} 

La definir la lógica " Acciones "que extienden el Comando (por ejemplo, Insertar):

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 
    } 
} 

Aplicando los patrones:

Este último paso define el comportamiento de deshacer/rehacer. La idea central es almacenar una pila de comandos que funcione como una lista histórica de los comandos. Para admitir rehacer, puede mantener un puntero secundario siempre que se aplique un comando de deshacer. Tenga en cuenta que cada vez que se inserta un objeto nuevo, se eliminan todos los comandos después de su posición actual; eso se logra mediante el método deleteElementsAfterPointer se definen a continuación:

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(); 
} 

Conclusión:

Lo que hace este diseño de gran alcance es el hecho de que se pueden añadir tantos comandos como desee (extendiendo la clase Command) por ejemplo, , RemoveCommand, UpdateCommand y así sucesivamente. Además, el mismo patrón es aplicable a cualquier tipo de objeto, haciendo que el diseño reutilizable y se pueda modificar en diferentes casos de uso.

+0

¿Cómo haría para limitar el número de pasos para deshacer/rehacer? No es posible dimensionar una pila. – Atlas2k

+0

@ Atlas2k puede tener un umbral (por ejemplo, 'int threshhold = 30') y luego eliminar todos los comandos" anteriores "en función del tamaño de la pila actual. Por ejemplo, si llama a 'insertCommand()' y su 'commandStack.size()' es mayor que 30, entonces elimina el primer elemento ('commandStack.remove (0)'). La idea principal es limitar tu stack a un determinado tamaño de umbral. Sí, caso de uso más complejo, pero definitivamente posible :) –

+0

Seguí esto para establecer mi tamaño máximo de pila: http://ntsblog.homedev.com.au/index.php/2010/05/06/c-stack- con límite máximo / – Atlas2k

Cuestiones relacionadas