Стек <Canvas>в документе JavaFX FXML - PullRequest
0 голосов
/ 23 сентября 2018

Я создаю программу для работы с изображениями в JavaFX, используя FXML для организации пользовательского интерфейса.В настоящее время он поддерживает такие вещи, как загрузка изображений, рисование и сохранение этих изменений.

Я не знаю, как / не могу найти способ представления стека Canvas 'в документе FXML.Моя цель - иметь стек Canvas, «который позволит мне отменить изменения, внесенные пользователем, просто очистив верхний слой в стеке холста».Идея состоит в том, что каждое редактирование будет находиться на своем собственном холсте.

Ниже приведен код из документа FXML.Вот центральная панель моей пограничной панели.Это центр программы для работы с изображениями.Внутри него у меня есть панель стека, чтобы я мог наложить вещи.Два комментария - это то, что я ожидал бы сделать, но эти попытки не работают.

<!-- center pane -->
<center>
    <StackPane>
        <Canvas fx:id="currCanvas" />
        <!-- <Canvas fx:id="canvasStack" /> -->
        <!-- <Stack fx:id="canvasStack" /> -->
        <Canvas fx:id="previewCanvas" />
   </StackPane>
</center>

Если бы я собирался реализовать это без документа FXML, это было бы намного проще, но это было бы болеетрудно организовать пользовательский интерфейс.Моя путаница в том, что я не знаю, как сделать это с помощью FXML.

Спасибо

Ответы [ 2 ]

0 голосов
/ 24 сентября 2018

Просто используйте Pane.Таким образом, нет проблем с выравниванием детей.Pane s не предоставляют Stack дочерних элементов, но List можно использовать и для операции со стеком, хотя удаление последнего элемента из списка немного сложнее, чем для стека, но достаточно просто.

Следующий код просто добавляет кружки и прямоугольники, но вы можете заменить это добавлением Canvas es:

@Override
public void start(Stage primaryStage) {
    Button doBtn = new Button("draw");
    Button undo = new Button("undo");
    undo.setDisable(true);

    Pane stack = new Pane();
    stack.setPrefSize(400, 400);

    VBox root = new VBox(new HBox(doBtn, undo), stack);

    doBtn.setOnAction(new EventHandler<ActionEvent>() {

        private int count = 0;

        @Override
        public void handle(ActionEvent event) {
            // create & add some node
            Node addedNode;
            switch (count % 2) {
                case 0:
                    addedNode = new Circle(count * 5 + 5, count * 5 + 5, 5, Color.BLUE);
                    break;
                case 1:
                    Rectangle rect = new Rectangle(count * 5, 390 - count * 5, 10, 10);
                    rect.setFill(Color.RED);
                    addedNode = rect;
                    break;
                default:
                    return;
            }
            stack.getChildren().add(addedNode);
            undo.setDisable(false);
            count++;
        }

    });

    undo.setOnAction(evt -> {
        // remove last child
        List<Node> children = stack.getChildren();
        children.remove(children.size() - 1);

        // check, if undo button needs to be disabled
        undo.setDisable(children.isEmpty());
    });

    Scene scene = new Scene(root);
    primaryStage.setScene(scene);
    primaryStage.show();
}

Обратите внимание, что я не рекомендовал бы создавать новыеCanvas е для каждой операции, так как это может привести к проблемам с памятью довольно быстро ...

0 голосов
/ 23 сентября 2018

Все, что вам действительно нужно, - это один Canvas, но всякий раз, когда выполняется редактирование, вы должны сделать снимок холста или сделать копию пикселей на холсте , а затем выдвинуть этот массив пикселей или объект, содержащийпиксель в стеке.Это было бы плохой идеей, потому что создание копии холста будет очень дорогим как в вычислительном отношении, так и с точки зрения памяти.Хотя это возможно, я бы советовал против этого.

Для меня лучший и самый простой способ справиться с "операциями отмены" - это сначала нарисовать на BufferedImage, а затем нарисовать это изображение на Canvas, как объяснено здесь и здесь и второе, чтобы ввести концепцию Action в ваше приложение.Например, «действие рисования» будет выглядеть примерно так:

interface Action 
{
    //Used to re-apply action after it was undone
    public void apply();

    //Undo the action
    public void undo();
}

class PaintAction() implements Action
{
    static class Pixel
    {
        final int x;
        final int y;
        final int oldColor;
        final int newColor;
        PixelPos(int x, int y, int oldColor, int newColor)
        {
            this.x = x;
            this.y = y;
            this.oldColor = oldColor;
            this.newColor = newColor;
        }
    }

    List<Pixel> affectedPixels;

    PaintAction()
    {
        affectedPixels = new ArrayList<>();
    }

    @Override
    public void apply(Canvas canvas)
    {
        for (Pixel pixel : pixel)
        {
            //draw new pixel's color on the canvas 
        }
    }

    @Override
    public void undo(Canvas canvas)
    {
        for (Pixel pixel : pixel)
        {
            //draw old pixel's color on the canvas 
        }
    }

    public void addPixel(Pixel pixel)
    {
        affectedPixels.add(Pixel);
    }
}

Поэтому, когда пользователь нажимает кнопку мыши, чтобы начать рисовать, вы создаете новый PaintAction, тогда всякий раз, когда пользователь перемещает мышь, вы бысоздайте новый объект Pixel и добавьте его в список «затронутых пикселей с помощью PaintAction», затем перейдите к изменению цвета пикселя в BufferedImage.

Тогда все, что вам нужно, этооставьте стопку Action и примените, отмените их, как считаете нужным.

Надеюсь, что это имеет смысл, ура.

...