JavaFX,列表到 ObservableList 到 ListView
JavaFX, List to ObservableList to ListView
所以我的问题是,我有一个序列化的 ArrayList,必须在我的 GUI 中更新它才能在 ListView 中动态显示它的内容。
序列化和反序列化在使用 DAO 接口时工作正常,但 GUI 不会刷新我的 ListView。
这个class保存我的数据交互(主要是保存,加载...):
public class Medienverwaltung implements Serializable, IDAO{
private static final long serialVersionUID = 1L;
private List<Medium> medienliste;
public ObservableList<Medium> obList; //public for test-reasons
public Medienverwaltung(){
medienliste = new ArrayList<Medium>();
obList = FXCollections.observableArrayList(medienliste);
}
//[...]
public List<Medium> getMedienliste(){
return this.medienliste;
}
//[...]
}
这是我的 GUI 实现片段:
public class HauptFenster extends Application{
private Medienverwaltung medienverwaltung;
@Override
public void start(Stage stage) throws Exception{
medienverwaltung = new Medienverwaltung();
VBox root = new VBox();
ListView<String> showliste = new ListView<String>();
MenuBar menuBar = createMenuBar(stage);
root.getChildren().add(menuBar);
root.getChildren().add(showliste);
//Make Listener and refresh the shown list!
medienverwaltung.obList.addListener(new ListChangeListener<Medium>(){
@Override
public void onChanged(ListChangeListener.Change<? extends Medium> change) {
showliste.getItems().clear();
for(Medium medium : medienverwaltung.obList){
//toString() is overwritten and works, too
showliste.getItems().add(medium.toString());
}
}
});
// this adds a Medium object to the Arraylist in Medienverwaltung
medienverwaltung.aufnehmen(new Bild("Foto12", 2017, "Zuhause"));
stage.setTitle("Medien Verwaltung");
stage.setScene(new Scene(root, 800, 400) );
stage.show();
}
//[...]
我也厌倦了用 ObservableList 交换 class "Medienverwaltung" 中的整个 ArrayList,这样就只剩下一个 List,它适用于 GUI 但不适用于序列化和反序列化正如我之前猜测的那样。 (并尝试了其他一些实现)
有谁知道如何更改我的代码以使其正常工作?
我的第二个问题是,就 3 层架构而言,最好的方法是什么?
以下是对 Fabians Answer 的引用,并回应了我对此的评论
更新#1.1(解释附录)
public interface IDAO {
// Save method
void speichern(List<Medium> liste) throws PersistenzException;
// Load method
List<Medium> laden() throws PersistenzException;
}
我的具体保存方法来了:
@Override
public void speichern(List<Medium> medienliste) throws PersistenzException{
File sfile = new File("medienliste.dat");
try(FileOutputStream fos = new FileOutputStream(sfile); ObjectOutputStream oos = new ObjectOutputStream(fos)){
oos.writeObject(medienliste);
System.out.println("Serialisierung erfolgreich!");
}catch(IOException e){
e.printStackTrace();
System.out.println("Serialisierung fehlgeschlagen!");
}
}
更新#1.2(解释附录)
//[...] section of my GUI for saving
MenuItem speichern = new MenuItem("Speichern");
speichern.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent e){
try{
//Before: medienverwaltung.speichern(medienverwaltung.getMedienliste()); -> doesn't work because of serializing an ObservableList
medienverwaltung.speichern(medienverwaltung.getBackingList());
}catch(PersistenzException pe){
pe.printStackTrace();
}
}
});
//[...]
但我猜想,以这种方式访问 backinlist 并不是一个好方法。
更新#2:
为了以干净的方式尊重封装原则,我现在在 class Medienverwaltung 中添加了一个重载方法:
public void speichern() throws PersistenzException{
speichern(backingList);
}
所以我的 GUI 现在只调用 speichern()。这实际上调用了使用无法从外部访问的备份列表进行保存的方法。我希望这不是糟糕的编码风格 ^^
顺便说一句:如果您正在阅读本文并遇到类似问题,请不要使用 ObservableArrayList 与普通 List[=53= 进行同步】,这样不行!请改用 ObservableList。
通过删除 getter 对其他 类 隐藏支持列表 (medienliste
)。如果您使用 ObservableList
修改此列表,ListView
(或已将侦听器添加到列表的所有其他对象)将正确更新。
此外,除非 Medium
扩展 Node
,您可以简单地将这种对象用作 ListView
的项目,因为单元格将文本设置为 [=22] 的结果=] 方法默认调用关联项。
public class Medienverwaltung implements Serializable, IDAO{
private static final long serialVersionUID = 1L;
private List<Medium> backingList;
// transient field not persisted
private transient ObservableList<Medium> medienliste;
public Medienverwaltung(){
backingList = new ArrayList<Medium>();
medienliste = FXCollections.observableArrayList(backingList);
}
// make sure an ObservableList is created when reading the serialized object
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
inputStream.defaultReadObject();
medienliste = FXCollections.observableArrayList(backingList);
}
//[...]
public ObservableList<Medium> getMedienliste(){
return this.medienliste;
}
//[...]
}
@Override
public void start(Stage stage) throws Exception{
medienverwaltung = new Medienverwaltung();
VBox root = new VBox();
ListView<Medium> showliste = new ListView<>(medienverwaltung.getMedienliste());
MenuBar menuBar = createMenuBar(stage);
root.getChildren().add(menuBar);
root.getChildren().add(showliste);
// this adds a Medium object to the Arraylist in Medienverwaltung
medienverwaltung.aufnehmen(new Bild("Foto12", 2017, "Zuhause"));
stage.setTitle("Medien Verwaltung");
stage.setScene(new Scene(root, 800, 400) );
stage.show();
}
请注意,Medienverwaltung.aufnehmen
方法不应直接与支持列表一起使用 - 它应该使用 ObservableList
来确保可以观察到更改...
编辑
查看 IDAO
接口,它可能应该是一个不同于 Medienverwaltung
的对象,否则您将违反 关注点分离 设计原则;将值作为参数传递也没有意义,该参数已经作为对象本身的 属性 包含。
似乎 IDAO
对象应该只负责 reading/writing 列表数据,这使得使用 Medienverwaltung
实现 Serializable
变得不必要。可能像这样的东西是你练习的预期解决方案:
IDAO idao = new IDAOImplementation();
Medienverwaltung medienverwaltung = new Medienverwaltung(idao.laden());
public void handle(ActionEvent e){
try{
idao.speichern(medienverwaltung.getMedienliste());
}catch(PersistenzException pe){
pe.printStackTrace();
}
}
public Medienverwaltung(List<Medium> medien) {
this.medienliste = FXCollections.observableArrayList(medien);
}
IDAO
的实现很可能不依赖于 List
的实现,因此不期望 List
是可序列化的。您可以简单地解决非序列化列表,方法是 a) 不使用 ObjectOutputStream
来保存数据,而是使用其他不依赖可序列化对象的方式,或者 b) 简单地将列表的内容复制到可序列化列表:
@Override
public void speichern(List<Medium> medienliste) throws PersistenzException{
File sfile = new File("medienliste.dat");
try(FileOutputStream fos = new FileOutputStream(sfile); ObjectOutputStream oos = new ObjectOutputStream(fos)){
oos.writeObject(new ArrayList(medienliste));
System.out.println("Serialisierung erfolgreich!");
} catch(IOException e){
throw new PersistenzException(e);
}
}
所以我的问题是,我有一个序列化的 ArrayList,必须在我的 GUI 中更新它才能在 ListView 中动态显示它的内容。 序列化和反序列化在使用 DAO 接口时工作正常,但 GUI 不会刷新我的 ListView。
这个class保存我的数据交互(主要是保存,加载...):
public class Medienverwaltung implements Serializable, IDAO{
private static final long serialVersionUID = 1L;
private List<Medium> medienliste;
public ObservableList<Medium> obList; //public for test-reasons
public Medienverwaltung(){
medienliste = new ArrayList<Medium>();
obList = FXCollections.observableArrayList(medienliste);
}
//[...]
public List<Medium> getMedienliste(){
return this.medienliste;
}
//[...]
}
这是我的 GUI 实现片段:
public class HauptFenster extends Application{
private Medienverwaltung medienverwaltung;
@Override
public void start(Stage stage) throws Exception{
medienverwaltung = new Medienverwaltung();
VBox root = new VBox();
ListView<String> showliste = new ListView<String>();
MenuBar menuBar = createMenuBar(stage);
root.getChildren().add(menuBar);
root.getChildren().add(showliste);
//Make Listener and refresh the shown list!
medienverwaltung.obList.addListener(new ListChangeListener<Medium>(){
@Override
public void onChanged(ListChangeListener.Change<? extends Medium> change) {
showliste.getItems().clear();
for(Medium medium : medienverwaltung.obList){
//toString() is overwritten and works, too
showliste.getItems().add(medium.toString());
}
}
});
// this adds a Medium object to the Arraylist in Medienverwaltung
medienverwaltung.aufnehmen(new Bild("Foto12", 2017, "Zuhause"));
stage.setTitle("Medien Verwaltung");
stage.setScene(new Scene(root, 800, 400) );
stage.show();
}
//[...]
我也厌倦了用 ObservableList 交换 class "Medienverwaltung" 中的整个 ArrayList,这样就只剩下一个 List,它适用于 GUI 但不适用于序列化和反序列化正如我之前猜测的那样。 (并尝试了其他一些实现)
有谁知道如何更改我的代码以使其正常工作? 我的第二个问题是,就 3 层架构而言,最好的方法是什么?
以下是对 Fabians Answer 的引用,并回应了我对此的评论
更新#1.1(解释附录)
public interface IDAO {
// Save method
void speichern(List<Medium> liste) throws PersistenzException;
// Load method
List<Medium> laden() throws PersistenzException;
}
我的具体保存方法来了:
@Override
public void speichern(List<Medium> medienliste) throws PersistenzException{
File sfile = new File("medienliste.dat");
try(FileOutputStream fos = new FileOutputStream(sfile); ObjectOutputStream oos = new ObjectOutputStream(fos)){
oos.writeObject(medienliste);
System.out.println("Serialisierung erfolgreich!");
}catch(IOException e){
e.printStackTrace();
System.out.println("Serialisierung fehlgeschlagen!");
}
}
更新#1.2(解释附录)
//[...] section of my GUI for saving
MenuItem speichern = new MenuItem("Speichern");
speichern.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent e){
try{
//Before: medienverwaltung.speichern(medienverwaltung.getMedienliste()); -> doesn't work because of serializing an ObservableList
medienverwaltung.speichern(medienverwaltung.getBackingList());
}catch(PersistenzException pe){
pe.printStackTrace();
}
}
});
//[...]
但我猜想,以这种方式访问 backinlist 并不是一个好方法。
更新#2:
为了以干净的方式尊重封装原则,我现在在 class Medienverwaltung 中添加了一个重载方法:
public void speichern() throws PersistenzException{
speichern(backingList);
}
所以我的 GUI 现在只调用 speichern()。这实际上调用了使用无法从外部访问的备份列表进行保存的方法。我希望这不是糟糕的编码风格 ^^
顺便说一句:如果您正在阅读本文并遇到类似问题,请不要使用 ObservableArrayList 与普通 List[=53= 进行同步】,这样不行!请改用 ObservableList。
通过删除 getter 对其他 类 隐藏支持列表 (medienliste
)。如果您使用 ObservableList
修改此列表,ListView
(或已将侦听器添加到列表的所有其他对象)将正确更新。
此外,除非 Medium
扩展 Node
,您可以简单地将这种对象用作 ListView
的项目,因为单元格将文本设置为 [=22] 的结果=] 方法默认调用关联项。
public class Medienverwaltung implements Serializable, IDAO{
private static final long serialVersionUID = 1L;
private List<Medium> backingList;
// transient field not persisted
private transient ObservableList<Medium> medienliste;
public Medienverwaltung(){
backingList = new ArrayList<Medium>();
medienliste = FXCollections.observableArrayList(backingList);
}
// make sure an ObservableList is created when reading the serialized object
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
inputStream.defaultReadObject();
medienliste = FXCollections.observableArrayList(backingList);
}
//[...]
public ObservableList<Medium> getMedienliste(){
return this.medienliste;
}
//[...]
}
@Override
public void start(Stage stage) throws Exception{
medienverwaltung = new Medienverwaltung();
VBox root = new VBox();
ListView<Medium> showliste = new ListView<>(medienverwaltung.getMedienliste());
MenuBar menuBar = createMenuBar(stage);
root.getChildren().add(menuBar);
root.getChildren().add(showliste);
// this adds a Medium object to the Arraylist in Medienverwaltung
medienverwaltung.aufnehmen(new Bild("Foto12", 2017, "Zuhause"));
stage.setTitle("Medien Verwaltung");
stage.setScene(new Scene(root, 800, 400) );
stage.show();
}
请注意,Medienverwaltung.aufnehmen
方法不应直接与支持列表一起使用 - 它应该使用 ObservableList
来确保可以观察到更改...
编辑
查看 IDAO
接口,它可能应该是一个不同于 Medienverwaltung
的对象,否则您将违反 关注点分离 设计原则;将值作为参数传递也没有意义,该参数已经作为对象本身的 属性 包含。
似乎 IDAO
对象应该只负责 reading/writing 列表数据,这使得使用 Medienverwaltung
实现 Serializable
变得不必要。可能像这样的东西是你练习的预期解决方案:
IDAO idao = new IDAOImplementation();
Medienverwaltung medienverwaltung = new Medienverwaltung(idao.laden());
public void handle(ActionEvent e){
try{
idao.speichern(medienverwaltung.getMedienliste());
}catch(PersistenzException pe){
pe.printStackTrace();
}
}
public Medienverwaltung(List<Medium> medien) {
this.medienliste = FXCollections.observableArrayList(medien);
}
IDAO
的实现很可能不依赖于 List
的实现,因此不期望 List
是可序列化的。您可以简单地解决非序列化列表,方法是 a) 不使用 ObjectOutputStream
来保存数据,而是使用其他不依赖可序列化对象的方式,或者 b) 简单地将列表的内容复制到可序列化列表:
@Override
public void speichern(List<Medium> medienliste) throws PersistenzException{
File sfile = new File("medienliste.dat");
try(FileOutputStream fos = new FileOutputStream(sfile); ObjectOutputStream oos = new ObjectOutputStream(fos)){
oos.writeObject(new ArrayList(medienliste));
System.out.println("Serialisierung erfolgreich!");
} catch(IOException e){
throw new PersistenzException(e);
}
}