XMLエディターを作成しましたが、最後のフェーズである元に戻す/やり直し機能の追加で立ち往生しています。
ユーザーが要素、属性、またはテキストをJTreeに追加する場合にのみ、元に戻す/やり直しを追加する必要があります。
私はまだこれに慣れていませんが、今日の学校では、元に戻すとやり直しと呼ばれる2つのスタックオブジェクト[]を作成し、実行されたアクションをそれらに追加しようとしました(失敗しました)。
たとえば、私は持っています:
Action AddElement() {
// some code
public void actionPerformed(ActionEvent e) {
performElementAction();
}
}
performElementActionは、実際に要素をJTreeに追加するだけです。
実行したこのアクションを元に戻すスタックに追加する方法を追加したいと思います。元に戻す簡単な方法はありますか?プッシュ(実行されたアクション全体)または何か?
TL; DR:コマンドパターンとMementoパターン(デザインパターン-Gamaet。al)を実装することで、元に戻すアクションとやり直しアクションをサポートできます。
Mementoパターン
この単純なパターンにより、オブジェクトの状態を保存できます。オブジェクトを新しいクラスでラップし、状態が変化するたびに更新するだけです。
public class Memento
{
MyObject myObject;
public MyObject getState()
{
return myObject;
}
public void setState(MyObject myObject)
{
this.myObject = myObject;
}
}
コマンドパターン
コマンドパターンには、元のオブジェクト(元に戻す/やり直しをサポートする必要がある)と、元に戻す場合に必要なmementoオブジェクトが格納されます。さらに、2つのメソッドが定義されています。
コード:
public abstract class Command
{
MyObject myObject;
Memento memento;
public abstract void execute();
public abstract void unExecute();
}
コマンドを拡張する論理的な「アクション」を定義します(例:挿入):
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
}
}
パターンの適用:
この最後のステップは、元に戻す/やり直しの動作を定義します。それらの中心的なアイデアは、コマンドの履歴リストとして機能するコマンドのスタックを格納することです。 REDOをサポートするために、元に戻すコマンドが適用されるたびに2次ポインターを保持できます。新しいオブジェクトが挿入されるたびに、現在の位置以降のすべてのコマンドが削除されることに注意してください。これは、以下に定義されているdeleteElementsAfterPointer
メソッドによって実現されます。
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();
}
結論:
この設計を強力なものにしているのは、(Command
クラスを拡張することにより)必要な数のコマンドを追加できるという事実です。たとえば、RemoveCommand
、UpdateCommand
などです。さらに、同じパターンをあらゆるタイプのオブジェクトに適用できるため、さまざまなユースケースでデザインを再利用可能および変更可能にすることができます。
コマンドパターン を見てください。その用途には、元に戻す/やり直し機能の実装が含まれます。
この チュートリアル は、コマンドパターンの基本とSwingの元に戻す/やり直しのメカニズムを説明しています。それが役に立てば幸い。
AddElementActionクラスがActionを継承して、Actionクラスを作成しようとしました。 AddElementActionには、それに応じて要素を追加/削除するDo()メソッドとUndo()メソッドを含めることができます。次に、元に戻す/やり直しのためにアクションの2つのスタックを保持し、ポップする前に一番上の要素でDo()/ Undo()を呼び出すだけです。
You have to define undo(), redo() operations along with execute() in Command interface itself
。
例:
interface Command {
void execute() ;
void undo() ;
void redo() ;
}
ConcreteCommandクラスで状態を定義します。 execute()メソッド後の現在の状態に応じて、コマンドをUndoStackとRedoStackのどちらに追加するかを決定し、それに応じて決定する必要があります。
理解を深めるために、この ndo-redo コマンドの記事をご覧ください。