2013-03-02 46 views
3

我正在研究一个JavaFX应用程序,让用户选择一个文件夹,然后解析它的内容以找到MP3文件并读取它们的元数据。线程更新进度条和多个标签

虽然我发现很难让用户界面看起来不错,但我仍然使用Swing工作。因此我试图在JavaFX中做同样的事情。

在原始的Swing应用程序中,我创建了一个线程,该线程开始解析用户选择的文件夹中的文件。它的工作原理是这样的:

  1. 找出文件总数解析 - 文件的数量和文件夹的数量在UI
  2. 解析在两个单独的标签不断提出的所有文件,以查找哪些MP3文件并存储元数据 - 发现的MP3文件的数量连续呈现在UI中的标签中

同时更新标签以显示最新情况的状态以及反映上述两个步骤的进展。第一步的进度占总进度的30%,而第二步占70%。

我发现了一个如何将任务绑定到进度条的示例,但我还需要更新四个标签:状态,文件数量,文件夹数量和MP3数量。

我相信我可以用updateMessage处理其中一个标签,但我不知道如何管理其他三个。

回答

8

使用多个任务将问题分成多个位。使用控制任务来监视子任务的状态和整体进度。使用java.util.concurrent类来管理Task执行,排序和数据结构,如LinkedBlockingDeque

这个推荐的解决方案并不是解决您的问题的最简单的解决方案,但如果做得好,应该提供良好的用户体验。


对于除法的一个例子而治之方法适用于不同的问题,请参阅下面的代码示例:

  1. splits a complex process into multiple managed subtasks
  2. demonstrates management of execution of multiple workers sequentially or in parallel

潜在简单替代的方法是使用一个单一的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); } 
} 

mp3finder


参见use of Platform.runLater and accessing the UI from a different thread for more information的StackOverflow的问题更多的信息和对JavaFX的并发链接到更多资源。

+0

感谢您的回答。这需要我花一些时间来测试;我以前没有使用'Platform.runLater'的经验。在swing中我使用了一个'SwingWorker',我可以使用'publish()'来不断更新任意数量的UI元素。 runLater会提供类似的东西吗? – nivis 2013-03-03 10:03:00

+0

我一直在试图找到一个很好的例子,当线程运行时如何使用'Platform.runLater'来更新GUI元素,但我还没有找到任何。你能指点我这样一个例子,或者提供一个如何做到这一点的代码示例?线程完成后,我发现的所有示例都会更新GUI。 – nivis 2013-03-03 20:41:59

+0

用请求的代码示例更新答案。 – jewelsea 2013-03-04 19:56:59