J'essaie de suivre MVC pour un projet de test, donc mon modèle devrait être complètement indépendant de ma vue, mais je ne suis pas sûr de la façon dont je devrais mettre à jour une liste observable qui obtient mis à jour dans un thread d'arrière-plan (il est donné chaînes de télécharger des fichiers via FTP) afin que les messages apparaissent sur l'interface utilisateur dans un ListView. J'utilise JavaFX et j'essaie d'obtenir un couplage le plus faible possible de mon programme. À ce moment, l'interface graphique dans le paquet de vue dépend du fait que mon modèle met à jour ma liste en utilisant Platform.runLater (...) - à ma connaissance, mon modèle devrait fonctionner complètement indépendamment de la vue, et shouldn ' Je dois me conformer aux besoins de View. Maintenant le code suivant "fonctionne comme prévu" il n'est tout simplement pas modélisé correctement, et je ne suis pas sûr comment je peux le modéliser correctement. Certaines recherches initiales ont montré que je devrais utiliser Observateur et observable - et avoir une autre classe au milieu pour agir comme ma liste observable - mais je ne suis pas sûr de la façon dont je mettrais cela en place.Correct façon de mettre à jour la liste Observable du thread d'arrière-plan
J'ai donc une liste Observable qui est mis à jour sur un fil de fond:
private ObservableList<String> transferMessages;
public FTPUtil(String host, int port, String user, String pass) {
this.host = host;
this.port = port;
this.username = user;
this.password = pass;
transferMessages = FXCollections.observableArrayList();
connect();
}
public void upload(File src) {
System.out.println("Uploading: " + src.getName());
try {
if (src.isDirectory()) {
ftpClient.makeDirectory(src.getName());
ftpClient.changeWorkingDirectory(src.getName());
for (File file : src.listFiles()) {
upload(file);
}
ftpClient.changeToParentDirectory();
} else {
InputStream srcStream = null;
try {
addMessage("Uploading: " + src.getName());
srcStream = src.toURI().toURL().openStream();
ftpClient.storeFile(src.getName(), srcStream);
addMessage("Uploaded: " + src.getName() + " - Successfully.");
} catch (Exception ex) {
System.out.println(ex);
addMessage("Error Uploading: " + src.getName() + " - Speak to Administrator.");
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void addMessage(String message){
Platform.runLater(() -> transferMessages.add(0, message));
}
La classe FTPUtil est mon modèle.
J'ai aussi un modèle de classe Manager qui est ce qui contrôle cette classe FTPUtil:
public class ModelManager {
private ObservableList<String> fileAndFolderLocations;
private FTPUtil ftpUtil;
public ModelManager(String host, int port, String user, String pass) {
ftpUtil = new FTPUtil(host, port, user, pass);
fileAndFolderLocations = FXCollections.observableArrayList();
}
public boolean startBackup() {
Task task = new Task() {
@Override
protected Object call() throws Exception {
System.out.println("I started");
ftpUtil.clearMessages();
for(String location : fileAndFolderLocations){
File localDirPath = new File(location);
ftpUtil.upload(localDirPath);
}
return null;
}
};
new Thread(task).start();
return true;
}
public void addFileOrFolder(String fileOrFolder){
if(!fileAndFolderLocations.contains(fileOrFolder)){
fileAndFolderLocations.add(fileOrFolder);
}
}
public boolean removeFileOrFolder(String fileOrFolder){
return fileAndFolderLocations.remove(fileOrFolder);
}
public ObservableList<String> getFilesAndFoldersList() {
return fileAndFolderLocations;
}
public ObservableList<String> getMessages() {
return ftpUtil.getMessages();
}
}
est enfin mon GUI:
public class BackupController {
private Main main;
private ModelManager mm;
@FXML
private ListView<String> messagesList;
@FXML
void forceBackup(ActionEvent event) {
mm.startBackup();
}
public void initController(Main main, ModelManager mm) {
this.main = main;
this.mm = mm;
messagesList.setItems(mm.getMessages());
}
}
ajouter une couche d'isolation: garder le modèle ignorant de la vue (qui est de ne pas utiliser runLater), gardez une liste séparée pour montrant les éléments, écouter les changements de l'origi nal messages et les propager à la liste séparée sur le thread fx (c'est-à-dire runLater ici) – kleopatra
@kleopatra Hey, comme par couche d'isolation, voulez-vous dire une classe qui contient simplement une autre liste observable, ou étend une liste observable -> est alors observe mon modèle (qui devrait être observable) pour quand sa liste change? – AntonyKimpton
même endroit que vous le faites maintenant, BackgroundController semble très bien pour la tâche – kleopatra