2013-03-02 73 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