上下文
我是JavaFX的新手,但是我正在尝试构建一个应用程序,其中一个基本功能是向用户显示某个目录中的所有文件夹,并在删除新文件夹或文件夹时自动更新视图在那个目录中。这些文件夹可以被视为对象(例如,在ListView中),用户应该能够与它们进行交互。
我想使用MVC架构构建该应用程序。
到目前为止我做了什么:
所以我有一个视图(fxml),一个控制器类和我的模型来处理我的app逻辑。我在模型中使用WatchDir example from Oracle作为辅助类,并在控制器中启动WatchService,如下所示:
@Override
public void initialize(URL location, ResourceBundle resources) {
this.config = loadConfig(configFile);
this.facade = new facade(config);
Path rootPath = Paths.get(config.getDlRootPath());
try {
// register WatchService
new WatchDir(rootPath, false).processEvents();
statusText(rootPath + "is now being watched for changes");
} catch (IOException e) {
statusError("Directory " + e.getLocalizedMessage() + " does not exist.");
}
}
在WatchDir方法中,processEvents()可以执行以下操作:
if (!recursive && (kind == ENTRY_CREATE)) {
// new folder was created
}
我的问题:
告诉我的控制器有什么变化并更新ListView的最佳/最优雅的方法是什么?我想保持MVC风格。
也欢迎不同的方法:)
答案 0 :(得分:4)
我将使用的方法是在WatchDir
中提供用于注册回调的方法。最简单的方法是使用Consumer
属性进行回调:
public class WatchDir {
private Consumer<Path> onCreate ;
public void setOnCreate(Consumer<Path> onCreate) {
this.onCreate = onCreate ;
}
// other callbacks as needed...
// ...
void processEvents() {
// ...
Path child = ... ; // file/folder that was created
if (! recursive && kind == ENTRY_CREATE) {
if (onCreate != null) {
onCreate.accept(child);
}
}
// ...
}
// ...
}
请注意WatchDir.processEvents()
是一个(非终止)阻塞调用,因此您需要在后台线程中运行它。所以从你的控制器你做:
WatchDir watchDir = new WatchDir(rootPath, false) ;
watchDir.setOnCreate(path ->
Platform.runLater(() -> listView.getItems().add(path)));
Thread watchThread = new Thread(watchDir::processEvents);
watchThread.setDaemon(true);
watchThread.start();
请注意,由于将在后台线程上调用回调,因此UI的更新应包含在Platform.runLater(...)
中。如果您愿意,可以使用WatchDir
装备Executor
来执行回调,这样您只需告诉它一次即可通过Platform.runLater(...)
执行回复:
public class WatchDir {
// Executor for notifications: by default just run on the current thread
private Executor notificationExecutor = Runnable::run ;
public void setNotificationExecutor(Executor notificationExecutor) {
this.notificationExecutor = notificationExecutor ;
}
private Consumer<Path> onCreate ;
// etc...
void processEvents() {
// ...
if (! recursive && kind == ENTRY_CREATE) {
if (onCreate != null) {
notificationExecutor.execute(() -> onCreate.accept(child));
}
}
// ...
}
}
然后
WatchDir watchDir = new WatchDir(rootPath, false) ;
watchDir.setNotificationExecutor(Platform::runLater);
watchDir.setOnCreate(listView.getItems()::add); /* or path -> listView.getItems().add(path) */
Thread watchThread = new Thread(watchDir::processEvents);
watchThread.setDaemon(true);
watchThread.start();