我正在开发一个JavaFX应用程序,让用户选择一个文件夹,然后解析它的内容以查找MP3文件并阅读它们的元数据。
我使用Swing工作虽然我发现很难让用户界面看起来很好。因此,我试图在JavaFX中做同样的事情。
在最初的Swing应用程序中,我创建了一个线程,该线程开始解析用户选择的文件夹中的文件。它的工作原理如下:
同时更新标签,显示发生的事件的状态,以及反映上述两个步骤进度的进度条。第一步的进度占总进度的30%,而第二步则占另外70%的进展。
我找到了一个如何将任务绑定到进度条的示例,但我还需要更新四个标签:状态,文件数,文件夹数和MP3数。
我相信我可以使用updateMessage
处理其中一个标签,但我不知道如何管理其他三个标签。
答案 0 :(得分:8)
使用多个任务将问题拆分为多个位。使用控制任务来监视子任务的状态和总体进度。使用java.util.concurrent类来管理Task执行,排序和数据结构,例如LinkedBlockingDeque。
这个推荐的解决方案不是解决您问题的最简单的解决方案,但如果做得好,应该提供良好的用户体验。
有关应用于不同问题的分而治之法的示例,请参阅以下代码示例:
一种潜在的简单替代方法是对整个过程使用单个Task
,并根据需要从任务代码中调用Platform.runLater
,将多个反馈值报告回JavaFX UI。
有关此方法的示例,请参阅Task documentation section "A Task Which Modifies The Scene Graph"。
这是在Platform.runLater
电话中同时更新多个标签的内容。
Platform.runLater(new Runnable() {
@Override public void run() {
status.setText("");
folderCount.setText("");
fileCount.setText("");
mp3Count.setText("");
}
});
和一些与你的例子相似的代码:
import java.util.Arrays;
import java.util.List;
import static javafx.application.Application.launch;
import javafx.application.*;
import javafx.beans.value.*;
import javafx.concurrent.Task;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class Mp3Finder extends Application {
final Label status = new Label();
final Label folderCount = new Label();
final Label fileCount = new Label();
final Label mp3Count = new Label();
@Override public void start(Stage stage) {
final GridPane finderResults = new GridPane();
finderResults.setPrefWidth(400);
finderResults.setVgap(10);
finderResults.setHgap(10);
finderResults.addRow(0, new Label("Status: "), status);
finderResults.addRow(1, new Label("# Folders: "), folderCount);
finderResults.addRow(2, new Label("# Files: "), fileCount);
finderResults.addRow(3, new Label("# mp3s: "), mp3Count);
final Button finderStarter = new Button("Find mp3s");
finderStarter.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent t) {
startMp3Finder(finderStarter);
}
});
VBox layout = new VBox(10);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10; -fx-font-size: 16;");
layout.getChildren().setAll(finderStarter, finderResults);
stage.setScene(new Scene(layout));
stage.show();
}
private void startMp3Finder(final Node starterNode) {
starterNode.setDisable(true);
Mp3FinderTask task = new Mp3FinderTask(status, folderCount, mp3Count);
task.runningProperty().addListener(new ChangeListener<Boolean>() {
@Override public void changed(ObservableValue<? extends Boolean> ov, Boolean wasRunning, Boolean isRunning) {
if (!isRunning) {
starterNode.setDisable(false);
}
}
});
final Thread thread = new Thread(task , "mp3-finder");
thread.setDaemon(true);
thread.start();
}
private class Mp3FinderTask extends Task<List<String>> {
private final Label status;
private final Label folderCount;
private final Label mp3Count;
public Mp3FinderTask(Label status, Label folderCount, Label mp3Count) {
this.status = status;
this.folderCount = folderCount;
this.mp3Count = mp3Count;
}
@Override protected List<String> call() throws Exception {
initFinderResults();
updateLabelLater(status, "Finding Folders");
setProgressIndicator(folderCount);
List folders = findFolders();
updateLabelLater(folderCount, folders.size() + "");
updateLabelLater(status, "Finding Files");
setProgressIndicator(fileCount);
List files = findFiles(folders);
updateLabelLater(fileCount, files.size() + "");
updateLabelLater(status, "Find mp3s");
setProgressIndicator(mp3Count);
List mp3s = findMp3s(files);
updateLabelLater(mp3Count, mp3s.size() + "");
updateLabelLater(status, "All mp3s Found");
return mp3s;
}
void updateLabelLater(final Label label, final String text) {
Platform.runLater(new Runnable() {
@Override public void run() {
label.setGraphic(null);
label.setText(text);
}
});
}
private List<String> findFolders() throws InterruptedException {
// dummy implementation
Thread.currentThread().sleep(1000);
return Arrays.asList("folder1", "folder2", "folder3");
}
private List<String> findFiles(List<String> folders) throws InterruptedException {
// dummy implementation
Thread.currentThread().sleep(1000);
return Arrays.asList("file1", "file2", "file3", "file4", "file5");
}
private List<String> findMp3s(List<String> files) throws InterruptedException {
// dummy implementation
Thread.currentThread().sleep(1000);
return Arrays.asList("music1", "music2");
}
private void initFinderResults() {
Platform.runLater(new Runnable() {
@Override public void run() {
status.setText("");
folderCount.setText("");
fileCount.setText("");
mp3Count.setText("");
}
});
}
private void setProgressIndicator(final Label label) {
Platform.runLater(new Runnable() {
@Override public void run() {
label.setGraphic(new ProgressIndicator());
}
});
}
}
public static void main(String[] args) { launch(args); }
}
有关更多信息,请参阅use of Platform.runLater and accessing the UI from a different thread for more information上的StackOverflow问题,以及指向JavaFX中有关并发性的更多资源的链接。