2016-11-16 5 views
2

Я пытаюсь реализовать undo/redo в JavaFX - рисую все свои фигуры с помощью graphicsContext(). Я огляделся и обнаружил, что существует метод save в графическом контексте, но он просто сохраняет атрибуты, а не фактическую форму/состояние холста. Какой был бы лучший способ обойти это?Внедрение Undo/Redo в 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 library] (https://github.com/TomasMikula/UndoFX) может помочь вам в решении вашей проблемы. Библиотека просто предоставляет абстрактный диспетчер состояний Undo/Redo, поэтому он не решает проблему из коробки, вам понадобится совсем немного кода в вашем приложении, чтобы соответствующим образом использовать его (например, такие вещи, как «EllipseDrawOperation» из решения fabian все равно потребуется, чтобы библиотека 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); 
    } 

} 

Проходят DrawBoard экземпляр вашего класса вместо Canvas и заменить

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

с

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

undo и redo перемещаться по истории.

+0

Это настолько полезно fabian, спасибо. У меня есть один вопрос. Я продолжаю получать ошибку, если я нарисую еще один круг (после того, как я начертил первый круг) - бросил на эту строку: 'operations.subList (historyIndex + 1, operations.size()). Clear();' Я получаю a: '" JavaFX Application Thread "java.lang.IllegalArgumentException: fromIndex (1)> toIndex (0)'. Я не слишком уверен, почему это происходит? – xn139

+0

@ xn139: Извините, но в настоящее время я не могу найти последовательность операций, чтобы воспроизвести это прямо сейчас. Кроме того: методы, изменяющие 'historyIndex' (' undo'/'redo' /' addDrawOperation')/список ('addDrawOperation'), должны гарантировать, что' historyIndex' всегда является допустимым индексом или '-1', поэтому' historyIndex < = size'. – fabian

+0

@fabian Рисунок с прозрачным цветом над чем-то другим ничего не меняет. Это цель прозрачного цвета. – mipa

Смежные вопросы