簡體   English   中英

在 JavaFx 上保存/加載窗格繪圖

[英]Save/Load a Pane drawing on JavaFx

我創建了一個繪畫應用程序,我在其中繪制形狀並將它們作為節點添加到窗格中。 類似於下面的代碼:

currentShape = new Rectangle();
shapeList.add(currentShape);
pane.getChildren().addAll(shapelist);

我想保存繪圖並能夠加載保存的繪圖。 我不確定如何處理這個問題。 我查看了使用以下類型的快照/可寫圖像的示例。 然而,這給了我一個Cannot Resolve SwingFXUtils錯誤

save.setOnAction(event -> {
            FileChooser fileChooser = new FileChooser();

            //Set extension filter
            fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("png files (*.png)", "*.png"));

            //Prompt user to select a file
            File f = fileChooser.showSaveDialog(null);

            if(f != null){
                try {
                    //Pad the capture area
                    WritableImage writableImage = new WritableImage((int) pane.getWidth(), (int) pane.getHeight());
                    pane.snapshot(null, writableImage);
                    RenderedImage renderedImage = SwingFXUtils.fromFXImage(writableImage, null);
                    //Write the snapshot to the chosen file
                    ImageIO.write(renderedImage, "png", f);
                } catch (IOException ex)
                { ex.printStackTrace(); }

            }
        });

如果有人對如何在窗格上保存和加載繪圖有更好的建議,我將不勝感激。

Slaw 是對的:目前最好的方法是創建您自己的 model 對象,代表您的應用程序中顯示的內容。

但是,如果您想嘗試直接寫入和讀取 JavaFX Shapes,您可以選擇:XML bean 序列化。

XML bean 序列化由XMLEncoderXMLDecoder類執行。

與常規的 Java 序列化不同,它們不查看字段,只查看 bean 屬性。 bean 屬性由公共讀取方法定義,這是一個以get開頭的零參數方法(或者,如果它返回原始boolean ,它可以選擇以is而不是get開頭)。

因此, getWidth()方法的存在定義了一個名為width的屬性。

如果有相應的set方法(在上述情況下為setWidth ),它只接受一個與 get 方法返回的相同類型的參數,則該屬性被定義為可寫屬性。

(完整的規則比這稍微復雜一些;我只描述了一般情況。完整的 JavaBeans 規范在此處。)

如果您看過 JavaFX 的javadoc ,您可能已經注意到 JavaFX 類定義了很多屬性。 這意味着你可以用這樣的東西保存你的pane孩子:

private static final java.nio.file.Path SAVE_FILE_LOCATION =
    Paths.get(System.getProperty("user.home"), "shapes.xml");

void save()
throws IOException {
    try (XMLEncoder encoder = new XMLEncoder(
        new BufferedOutputStream(
            Files.newOutputStream(SAVE_FILE_LOCATION)))) {

        encoder.setExceptionListener(e -> {
            throw new RuntimeException(e);
        });

        encoder.writeObject(pane.getChildren().toArray(new Node[0]));
    }
}

void load()
throws IOException {
    try (XMLDecoder decoder = new XMLDecoder(
        new BufferedInputStream(
            Files.newInputStream(SAVE_FILE_LOCATION)))) {

        decoder.setExceptionListener(e -> {
            throw new RuntimeException(e);
        });

        pane.getChildren().setAll((Node[]) decoder.readObject());
    }
}

但是,對於 class,XMLEncoder 可以知道和憑直覺知道的內容是有限的。例如,在上面的代碼中,我使用的是Node數組而不是原始 ObservableList,因為 XMLEncoder 不知道如何序列化任何 (未公開記錄)ObservableList 的具體實現。 然而,XMLEncoder 確實具有序列化 arrays 的內置能力,只要它可以序列化數組元素本身。

一個更重要的問題是它不知道如何序列化某些屬性並且只會忽略它們。 例如,Color 不是典型的 Java bean:它是只讀的,因此雖然 XMLEncoder 可以讀取其數據,但沒有設置方法,因此編碼器不知道要編寫哪些指令才能讓未來的 XMLDecoder 能夠用於創建等效顏色 object。

我們可以通過為它提供自定義 PersistenceDelegates來自定義 XMLEncoder。 方便的是, DefaultPersistenceDelegate子類允許將 bean 屬性名稱傳遞給構造函數,構造函數創建一個委托,告訴 XMLDecoder 尋找一個構造函數,該構造函數采用與原始寫入數據中的每個屬性對應的參數。

由於 Color 有一個四參數構造函數,它采用redgreenblueopacity屬性的值,我們可以將 DefaultPersistenceDelegate 添加到 XMLEncoder,它指示未來的 XMLDecoder 在重構 Color object 時使用這些屬性的值:

encoder.setPersistenceDelegate(Color.class,
    new DefaultPersistenceDelegate(
        new String[] { "red", "green", "blue", "opacity" }));

上面的意思是:“當編寫 Color object 時,為以后的解碼器編寫指令以在 Color class 中尋找一個構造函數,該構造函數需要四個雙精度值,然后通過調用 Color 對象的 getRed、getGreen、getBlue 寫入實際值以在將來傳遞, 和 getOpacity 方法。”

如果您希望您的形狀包含文本對象,您可以為字體class 添加持久性委托:

encoder.setPersistenceDelegate(Font.class,
    new DefaultPersistenceDelegate(
        new String[] { "name", "size" }));

您還可以為其他 Paint 實現添加持久性委托:

encoder.setPersistenceDelegate(LinearGradient.class,
    new DefaultPersistenceDelegate(new String[] {
        "startX", "startY", "endX", "endY",
        "proportional", "cycleMethod", "stops"
    }));
encoder.setPersistenceDelegate(RadialGradient.class,
    new DefaultPersistenceDelegate(new String[] {
        "focusAngle", "focusDistance", "centerX", "centerY",
        "radius", "proportional", "cycleMethod", "stops"
    }));

(我故意省略了 ImagePattern,因為雖然可以用 XML 表示一個圖像,但它丑陋且效率低下。如果你打算存儲圖像,XML 不是一個好的存儲格式。)

因此,加載和存儲方法的更新版本如下所示:

private static void addPersistenceDelegatesTo(Encoder encoder) {
    encoder.setPersistenceDelegate(Font.class,
        new DefaultPersistenceDelegate(
            new String[] { "name", "size" }));
    encoder.setPersistenceDelegate(Color.class,
        new DefaultPersistenceDelegate(
            new String[] { "red", "green", "blue", "opacity" }));
    encoder.setPersistenceDelegate(LinearGradient.class,
        new DefaultPersistenceDelegate(new String[] {
            "startX", "startY", "endX", "endY",
            "proportional", "cycleMethod", "stops"
        }));
    encoder.setPersistenceDelegate(RadialGradient.class,
        new DefaultPersistenceDelegate(new String[] {
            "focusAngle", "focusDistance", "centerX", "centerY",
            "radius", "proportional", "cycleMethod", "stops"
        }));
}

private static final java.nio.file.Path SAVE_FILE_LOCATION =
    Paths.get(System.getProperty("user.home"), "shapes.xml");

void save()
throws IOException {
    try (XMLEncoder encoder = new XMLEncoder(
        new BufferedOutputStream(
            Files.newOutputStream(SAVE_FILE_LOCATION)))) {

        encoder.setExceptionListener(e -> {
            throw new RuntimeException(e);
        });

        addPersistenceDelegatesTo(encoder);

        encoder.writeObject(pane.getChildren().toArray(new Node[0]));
    }
}

void load()
throws IOException {
    try (XMLDecoder decoder = new XMLDecoder(
        new BufferedInputStream(
            Files.newInputStream(SAVE_FILE_LOCATION)))) {

        decoder.setExceptionListener(e -> {
            throw new RuntimeException(e);
        });

        pane.getChildren().setAll((Node[]) decoder.readObject());
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM