Slaw 是对的:到目前为止,最好的方法是创建您自己的模型对象,这些对象代表您的应用程序中显示的内容。
但是,如果您想尝试直接编写和读取 JavaFX Shapes,您可以选择:XML bean 序列化。
XML bean 序列化由XMLEncoder 和XMLDecoder 类执行。
与常规 Java 序列化不同,它们不查看字段,只查看 bean 属性。 bean 属性由公共读取方法定义,它是一个以get 开头的零参数方法(或者,如果它返回原始boolean,它可以选择以is 开头而不是get )。
因此,getWidth() 方法的存在定义了一个名为 width 的属性。
如果有一个对应的set方法(在上面的例子中是setWidth),它只接受一个与get方法返回的相同类型的参数,则该属性被定义为可写属性.
(完整的规则比这复杂一点;我只描述了一般情况。完整的 JavaBeans 规范是here。)
如果您查看过 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());
}
}
然而,XMLEncoder 可以知道和直觉关于类的内容是有限的。例如,在上面的代码中,我使用了 Node 数组而不是原始的 ObservableList,因为 XMLEncoder 不知道如何序列化 ObservableList 的任何(未公开记录的)具体实现。但是,XMLEncoder 确实具有序列化数组的内置功能,只要它可以序列化数组元素本身。
一个更重要的问题是它不知道如何序列化某些属性并且会忽略它们。例如,Color 不是一个典型的 Java bean:它是只读的,所以虽然 XMLEncoder 可以读取它的数据,但没有设置方法,所以编码器不知道未来的 XMLDecoder 能够编写什么指令用于创建等效的 Color 对象。
我们可以通过providing it with custom PersistenceDelegates 自定义一个 XMLEncoder。方便的是,DefaultPersistenceDelegate 子类允许将 bean 属性名称传递给构造函数,该构造函数创建一个委托,该委托将告诉 XMLDecoder 寻找一个构造函数,该构造函数接受与原始写入数据中的每个属性对应的参数。
由于 Color 具有 a four-argument constructor,它采用 red、green、blue 和 opacity 属性的值,我们可以将 DefaultPersistenceDelegate 添加到 XMLEncoder,它指示未来的 XMLDecoders 使用这些属性' 重构 Color 对象时的值:
encoder.setPersistenceDelegate(Color.class,
new DefaultPersistenceDelegate(
new String[] { "red", "green", "blue", "opacity" }));
上面的意思是:“当写一个Color对象时,写指令让未来的解码器在Color类中寻找一个接受四个双精度值的构造函数,然后通过调用Color对象的getRed写出实际值在未来传递, getGreen、getBlue 和 getOpacity 方法。”
如果您希望您的形状将包含 Text 对象,您可以为 Font 类添加持久性委托:
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表示一个Image,但它很丑而且效率很低。如果你打算存储图像,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());
}
}