JavaFX,List to ObservableList to ListView

时间:2017-08-05 00:03:24

标签: serialization arraylist javafx dao observablelist

所以我的问题是,我有一个加强的ArrayList并且必须在我的GUI中更新它以动态地在ListView中显示它的内容。 序列化和反序列化可以使用DAO接口正常工作,但GUI不会刷新我的ListView。

这个类保存我的数据交互(主要是保存,加载......):

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();   
    }
    //[...]

我也厌倦了从班级交换整个ArrayList&#34; Medienverwaltung&#34;使用ObservableList,这样只剩下一个List,它适用于GUI,但不适用于我之前猜测的序列化和反序列化。 (并尝试了其他一些实现)

有谁知道如何更改我的代码以使其有效? 我的第二个问题是,就3层架构而言,最好的方法是什么?

以下是对Fabians的回应,并回应我对此的评论

更新#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();
            }
        }
    });
//[...]

但正如我猜测的那样,以这种方式访问​​后台列表并不是一个好方法。

更新#2:

以干净的方式尊重封装的原则我现在在Medienverwaltung类中添加了一个重载的方法:

public void speichern() throws PersistenzException{
    speichern(backingList);
}

所以我的GUI现在只调用speichern()。这实际上调用了使用已从外部访问的已备份列表进行保存的方法。我希望这不是错误的编码风格^^

BTW:如果您正在阅读此问题并遇到类似问题,请不要使用 ObservableArrayList 与普通的列表进行同步,这会赢得&#39工作!改为使用 ObservableList

1 个答案:

答案 0 :(得分:0)

通过删除getter来隐藏其他类中的支持列表(medienliste)。如果使用ObservableList修改此列表,ListView(或已添加监听器的所有其他对象)将正确更新。

此外,除非Medium扩展Node,否则您只需将此类对象用作ListView的项目,因为单元格会将文本设置为toString的结果默认情况下为相关项调用的方法。

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对象应该只负责读取/写入列表数据,这将使得SerializableMedienverwaltung的实现变得不必要。这样的事情可能是您锻炼的预期解决方案:

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