简体   繁体   English

堆 <Canvas> 在JavaFX FXML文档中

[英]Stack<Canvas> in a JavaFX FXML document

I'm creating an image manipulation program in JavaFX using FXML to organize the UI. 我正在使用FXML在JavaFX中创建图像处理程序来组织UI。 It currently supports things like loading images, drawing, and saving those changes. 当前,它支持诸如加载图像,绘图和保存这些更改的操作。

I do not know how/cannot find a way to represent a Stack of Canvas' in the FXML document. 我不知道如何/找不到在FXML文档中表示画布堆栈的方法。 My goal is to have a stack of Canvas' that will allow me to undo the changes the user has made by simply clearing the top layer in the stack of canvas'. 我的目标是拥有一堆“画布”,这将使我能够通过简单地清除画布堆栈中的顶层来撤消用户所做的更改。 The idea is that each edit would reside on it's own canvas. 想法是,每次编辑都将驻留在其自己的画布上。

Below is some code from the FXML document. 以下是FXML文档中的一些代码。 Here is the center pane of my border pane. 这是我的边框窗格的中央窗格。 This is the center of the image manipulating program. 这是图像处理程序的中心。 Inside of it I have a stack pane so that I can overlay things. 在它内部,我有一个堆栈窗格,以便可以覆盖内容。 The two comments are what I would expect to be able to do but those attempts do not work. 这两条评论是我希望能够做的,但是这些尝试没有用。

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

If I was going to implement this without the FXML document it would be much simpler but it would be more difficult to organize the UI. 如果我要在没有FXML文档的情况下实现这一点,它将简单得多,但组织UI会更加困难。 My confusion is that I do not know how to accomplish this using FXML. 我的困惑是我不知道如何使用FXML来完成此任务。

Thanks 谢谢

All you really need is one Canvas but whenever an edit is made, you should make a snapshot of the canvas or make a copy of the pixels in the canvas then push that array of pixels or object containing the pixel to a Stack. 您真正需要的只是一个Canvas,但是每当进行编辑时,都应制作画布快照或复制画布中的像素,然后将像素阵列或包含像素的对象推入堆栈。 That would be a bad idea because making a copy of the canvas will be very expensive both computationally and memory wise. 这将是一个坏主意,因为复制画布将在计算和内存方面都非常昂贵。 While feasible, I would advise against it. 虽然可行,但我建议不要这样做。

To me the best and easiest way to deal with "undo operations" is to draw first on a BufferedImage then draw that image on the Canvas as explained here and here and second to introduce the concept of Action in your application. 对我来说,处理“撤消操作”的最好,最简单的方法是,首先在BufferedImage绘制,然后按照此处此处所述在Canvas上绘制该图像,然后在您的应用程序中引入Action的概念。 For instance a "paint action" would look something like: 例如,“绘画动作”将类似于:

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);
    }
}

So when the user presses the mouse button to start painting, you create a new PaintAction then whenever the user moves the mouse, you would create a new Pixel object and add it to the list of "affected pixels by the PaintAction " then proceed to change the color of the pixel in the BufferedImage . 因此,当用户按下鼠标按钮开始绘画时,您将创建一个新的PaintAction然后每当用户移动鼠标时,您都将创建一个新的Pixel对象并将其添加到“ PaintAction影响的像素”列表中,然后继续进行更改BufferedImage像素的颜色。

Then all you would need will be to keep a stack of Action and apply, undo them as you see fit. 然后,您需要做的就是保留一堆Action并应用,并在您认为合适时撤消它们。

Hope that make sense, cheers. 希望有道理,欢呼。

Simply use a Pane . 只需使用Pane This way there are no issues with aligning the children. 这样,对齐孩子就没有问题了。 Pane s don't provide a Stack of children, but a List can be used for stack operation too, although the removing the last item from the list is a bit more difficult than for a stack, but simple enough. Pane没有提供子级Stack ,但是List也可以用于堆栈操作,尽管从列表中删除最后一个项目比堆栈要困难一些,但是很简单。

The following code simply adds circles and rectangles, but you could replace this with adding Canvas es instead: 以下代码仅添加了圆形和矩形,但是您可以通过添加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();
}

Note that I wouldn't recommend creating new Canvas es for every operation though, since this could lead to memory issues pretty fast... 请注意,尽管如此,我不建议您为每个操作创建新的Canvas es,因为这可能会导致内存问题相当快...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM