2016-11-16 20 views
2

JavaFXで取り消し/やり直しを実装しようとしています。私はgraphicsContext()を使ってすべての形状を描画します。私は周囲を見渡して、Graphics Contextにsaveメソッドがあることを発見しましたが、キャンバスの実際の形状/状態ではなく属性を保存するだけです。これについて最善の方法は何でしょうか?私は、例えば、円を作成するときJavaFXでの取り消し/やり直しの実装

これは私のコードスニペットのいずれかです。

public CircleDraw(Canvas canvas, Scene scene, BorderPane borderPane) { 
     this.borderPane = borderPane; 
     this.scene = scene; 
     this.graphicsContext = canvas.getGraphicsContext2D(); 

     ellipse = new Ellipse(); 
     ellipse.setStrokeWidth(1.0); 
     ellipse.setFill(Color.TRANSPARENT); 
     ellipse.setStroke(Color.BLACK); 

     pressedDownMouse = event -> { 
      startingPosX = event.getX(); 
      startingPosY = event.getY(); 
      ellipse.setCenterX(startingPosX); 
      ellipse.setCenterY(startingPosY); 
      ellipse.setRadiusX(0); 
      ellipse.setRadiusY(0); 
      borderPane.getChildren().add(ellipse); 

     }; 

     releasedMouse = event -> { 
      borderPane.getChildren().remove(ellipse); 
      double width = Math.abs(event.getX() - startingPosX); 
      double height = Math.abs(event.getY() - startingPosY); 
      graphicsContext.setStroke(Color.BLACK); 
      graphicsContext.strokeOval(Math.min(startingPosX, event.getX()), Math.min(startingPosY, event.getY()), width, height); 
     removeListeners(); 
     }; 

     draggedMouse = event -> { 
      ellipse.setCenterX((event.getX() + startingPosX)/2); 
      ellipse.setCenterY((event.getY() + startingPosY)/2); 
      ellipse.setRadiusX(Math.abs((event.getX() - startingPosX)/2)); 
      ellipse.setRadiusY(Math.abs((event.getY() - startingPosY)/2)); 

     }; 

    } 
+2

[UndoFXライブラリ](https://github.com/TomasMikula/UndoFX)は、問題の解決に役立つかもしれません。ライブラリは抽象的なUndo/Redo状態マネージャを提供するだけなので、そのままの状態で問題を解決することはできません。アプリケーション内でカスタムコードをかなり必要とします(例えば、ファビアンのソリューションからの 'EllipseDrawOperation'は依然として必要です.UndoFXライブラリは、そのような操作の履歴を保存して操作する場所を提供するだけです)。 – jewelsea

答えて

4

ここでの問題は、このような情報がCanvasに保存されていないことがあるということです。さらに、描画情報ごとに前の状態に戻ることを可能にする逆の操作はありません。もちろん、同じ楕円形を描くことはできますが、元の図形情報からの情報は上書きされている可能性があります。複数の交差する楕円を描画している場合。

ただし、コマンドパターンを使用して描画操作を保存することもできます。これにより、すべてを再描画することができます。

public interface DrawOperation { 
    void draw(GraphicsContext gc); 
} 

public class DrawBoard { 
    private final List<DrawOperation> operations = new ArrayList<>(); 
    private final GraphicsContext gc; 
    private int historyIndex = -1; 

    public DrawBoard(GraphicsContext gc) { 
     this.gc = gc; 
    } 

    public void redraw() { 
     Canvas c = gc.getCanvas(); 
     gc.clearRect(0, 0, c.getWidth(), c.getHeight()); 
     for (int i = 0; i <= historyIndex; i++) { 
      operations.get(i).draw(gc); 
     } 
    } 

    public void addDrawOperation(DrawOperation op) { 
     // clear history after current postion 
     operations.subList(historyIndex+1, operations.size()).clear(); 

     // add new operation 
     operations.add(op); 
     historyIndex++; 
     op.draw(gc); 
    } 

    public void undo() { 
     if (historyIndex >= 0) { 
      historyIndex--; 
      redraw(); 
     } 
    } 

    public void redo() { 
     if (historyIndex < operations.size()-1) { 
      historyIndex++; 
      operations.get(historyIndex).draw(gc); 
     } 
    } 
} 

class EllipseDrawOperation implements DrawOperation { 

    private final double minX; 
    private final double minY; 
    private final double width; 
    private final double height; 
    private final Paint stroke; 

    public EllipseDrawOperation(double minX, double minY, double width, double height, Paint stroke) { 
     this.minX = minX; 
     this.minY = minY; 
     this.width = width; 
     this.height = height; 
     this.stroke = stroke; 
    } 

    @Override 
    public void draw(GraphicsContext gc) { 
     gc.setStroke(stroke); 
     gc.strokeOval(minX, minY, width, height); 
    } 

} 

Canvasするのではなく、自分のクラスに DrawBoardインスタンスを渡すと歴史を移動する

drawBoard.addDrawOperation(new EllipseDrawOperation(
          Math.min(startingPosX, event.getX()), 
          Math.min(startingPosY, event.getY()), 
          width, 
          height, 
          Color.BLACK)); 

undoredo

graphicsContext.setStroke(Color.BLACK); 
graphicsContext.strokeOval(Math.min(startingPosX, event.getX()), Math.min(startingPosY, event.getY()), width, height); 

を交換してください。

+0

これは非常に有用なファビアンです、ありがとう - 私は一つの質問があります。この行にスローされた別のサークル(最初のサークルを描いた後)を描画するとエラーが発生します: 'operations.subList(historyIndex + 1、operations.size())。clear();' a:「JavaFXアプリケーションスレッド」java.lang.IllegalArgumentException:fromIndex(1)> toIndex(0) '。なぜこれが起こっているのか分からないのですか? – xn139

+0

@ xn139:申し訳ありませんが、現在のところ、現在この作業を再現する一連の操作が見つかりません。また、 'historyIndex'(' undo'/'redo' /' addDrawOperation')/リスト( 'addDrawOperation')サイズを変更するメソッドは、' historyIndex'が常に有効なインデックスまたは '-1'であることを保証する必要があります。したがって、' historyIndex < =サイズは保証されるべきである。 – fabian

+0

@ fabian何かの上に透明な色で描いても何も変わりません。それが透明な色の目的です。 – mipa

関連する問題