ツール系アプリにあると何かと便利なアンドゥ・リドゥ機能の実装について考えてみる。
こういった機能は汎用的に作っておくと使い回しが利くのでクラスにしておくと良い。
このクラスに最低限必要なのは、今までに行った操作を順に覚えておくことと、適切に操作の戻し/進みが出来ることである。
操作を一つのリンクトリストで管理してもいいのだが、アンドゥ用とリドゥ用の2つのスタックで管理すると
シンプルに実装できるのではないだろうか。
次に、必要なメソッドを考えてみる。
- アンドゥ() ・・・ 操作を一つ戻す
- リドゥ() ・・・ 戻した操作をもう一度元に戻す
- 履歴の追加() ・・・ 行った操作を履歴に追加する
- 履歴一覧の取得() ・・・ 履歴の一覧を取得する
アンドゥのスタックに要素があるならば、popし、リドゥのスタックにpushして要素を返す。
リドゥのスタックに要素があるならば、popし、アンドゥのスタックにpushして要素を返す。
アンドゥのスタックに操作をpushし、リドゥのスタックはクリアする。
アンドゥのスタックの一覧を返す。
以上で最低限の機能は満たせそうである。
これらの機能をコードにすると以下のようになる。
HistoryStack.java
package com.lesson.undo; import java.util.Stack; public class HistoryStack<T> { private final Stack<T> undoStack = new Stack<T>(); private final Stack<T> redoStack = new Stack<T>(); /** * アンドゥ * @return */ public T undo(){ T result = null; if( !undoStack.empty() ){ result = undoStack.pop(); redoStack.push(result); } return result; } /** * リドゥ * @return */ public T redo(){ T result = null; if( !redoStack.empty() ){ result = redoStack.pop(); undoStack.push(result); } return result; } /** * 履歴の追加 * @param history */ public void add(T history){ undoStack.push(history); redoStack.clear(); } /** * アンドゥの列挙 * @return */ public final Iterable<T> iterateUndo(){ return undoStack; } }
次に、このクラスを使ったテストプログラムを書いてみよう。
Main.java
package com.lesson.undo; public class Main { /** * Iterableのプリント * @param ite */ private static <T> void printIterable(final Iterable<T> ite){ for(T i:ite){ System.out.print(i + " "); } System.out.println(); } /** * @param args */ public static void main(String[] args) { HistoryStack<Integer> history = new HistoryStack<Integer>(); // 1~5まで履歴を追加 history.add(1); history.add(2); history.add(3); history.add(4); history.add(5); System.out.println("1~5まで履歴を追加"); printIterable(history.iterateUndo()); // 2回undoする history.undo(); history.undo(); System.out.println("2回undoする"); printIterable(history.iterateUndo()); // 1回redoする history.redo(); System.out.println("1回redoする"); printIterable(history.iterateUndo()); // 履歴6を追加 history.add(6); System.out.println("履歴6を追加"); printIterable(history.iterateUndo()); } }
このテストプログラムを実行すると以下のように表示される。
1~5まで履歴を追加
1 2 3 4 5
2回undoする
1 2 3
1回redoする
1 2 3 4
履歴6を追加
1 2 3 4 6
アンドゥ・リドゥ機能が正しく働いていることが分かる。
今回作ったHistoryStackクラスはジェネリクスを用いて、どんな型でも格納できるようにしてある。
テストプログラムでは簡単のためInteger型を用いたが、実際には操作を表すコマンドクラスのようなものを作って
格納していくことになる。
ピンバック: 【Android】アンドゥ・リドゥ機能の組み込み – ザワプロ!
ピンバック: アンドゥ・リドゥ機能をProcessingで実装してみた【Java】 | シャラグのやってやりましょう