2015-09-15 71 views
-1

我正在嘗試構建一個進度條,該進度條正在更新,因爲我的應用程序正在檢索數據並將數據填充到GUI。我發現進度條會被重用很多,所以我決定創建自己的類。但是,我不相信我理解工人/任務或者多線程來創建可重用的情況。推薦的方法是創建一個可以偵聽應用程序線程並相應地更新欄的進度欄。這是我的嘗試:在JavaFx中構建多線程ProgressBar

// Simple Progress Bar View as Pop Up 
public class ProgressIndicatorUtil{ 

    @FXML 
    private ProgressBar progressBar; 
    @FXML 
    private Label statusLabel; 
    @FXML 
    private Button closeButton; 
    @FXML 
    private Label valueLabel; 

    private Worker worker; 
    private Stage stage; 

    public void setPopUpStage(Stage stage) { 
     this.stage = stage; 
    } 

    public void setWorker(Worker worker) { 
     this.worker = worker; 
    } 

    public void setLinkToMainPage(Object controller) { 

     ((Task<String>) worker).setOnSucceeded(event -> stage.close()); 

     ((Task<String>) worker).setOnCancelled(event -> { 
       closeButton.setVisible(true); 
       stage.requestFocus(); 
       statusLabel.setTextFill(Color.RED);} 
     ); 

     valueLabel.textProperty().bind(Bindings.format("%5.1f%%", worker.progressProperty().multiply(100))); 
     progressBar.progressProperty().bind(worker.progressProperty()); 
     statusLabel.textProperty().bind(worker.messageProperty()); 
    } 

    @FXML 
    private void handleClose(ActionEvent e){ 
     stage.close(); 
    } 
} 

調用視圖彈出窗口並運行進度條線程的控制器。

public class MyController{ 

    //Controller calling the view and disabling the main GUI 
    private void loadProgressBar(Worker worker){ 

     try{ 

      FXMLLoader loader = new FXMLLoader(getClass() 
        .getClassLoader().getResource("main/resources/fxml/ProgressBar.fxml")); 
      AnchorPane pane = (AnchorPane)loader.load(); 
      Stage popUpStage = new Stage(); 
      popUpStage.initModality(Modality.WINDOW_MODAL); 
      Scene scene = new Scene(pane); 
      popUpStage.setScene(scene); 

      ProgressIndicatorUtil controller = loader.getController(); 
      controller.setPopUpStage(popUpStage); 
      controller.setWorker(worker); 
      controller.setLinkToMainPage(this); 
      mainPane.setDisable(true); 
      popUpStage.showingProperty().addListener((obs, hidden, showing) -> { 
        if(hidden) mainPane.setDisable(false);}); 
      popUpStage.show(); 

     }catch(IOException e){ 
      e.printStackTrace(); 
     } 
    } 



    private void runProgressBar(Worker worker) { 
     new Thread((Runnable) worker).start(); 
    } 


    //A user action that runs the progress bar and GUI 
    @FXML 
    private void aBigProcessingEvent(ActionEvent event) { 

     Worker worker = new Task<String>(){ 

      @Override 
      protected String call() throws Exception {   

       updateProgress(0, 3); 
       updateMessage("Clearing Data"); 

       processingEvent01(); 

       updateProgress(1, 3); 
       updateMessage("Retriving Data And Adding To List"); 

       processingEvent02(); 

       updateProgress(2, 3); 
       updateMessage("Populating Data"); 

       processingEvent03(); 

       updateProgress(3, 3); 
       return "Finished!"; 
      } 
     }; 

     loadProgressBar(worker); 
     runProgressBar(worker); 
    } 
} 

程序工作正常,在視覺上,但它拋出這樣一個異常(Not On FX Application Thread)和我的「processingEvent」方法運行Platform.runLater()會導致我的進度條,立即100%,但不會拋出異常。任何有關如何分離應用程序修改方法和工作者方法,同時保持連接到processingEvent方法的進程的建議?非常感謝。

+0

您可以嘗試並創建一個Integer對象,然後將其傳遞給線程並在任務完成時增加。然後將您的進度設置爲integer /#任務的值。確保在訪問對象的引用時使用同步。 –

+0

@MarcinDeszczynski你不能那樣做:它肯定會產生OP描述的那種IllegalStateException異常。綁定到任務的進度屬性並調用'updateProgress(...)'是完成此任務的正確方法。 –

+0

這段代碼看起來很適合我;我無法測試它,因爲它不完整。哪一行會拋出'IllegalStateException'? 'processingEvent *()'方法有什麼作用?你應該在後臺線程中保留任何耗費時間的方法,但是包含所有更新「Platform.runLater(...)'中的UI的方法調用。(該'updateProgress'和'updateMessage'方法旨在從後臺線程調用;他們基本上處理對'Platform.runLater(...)'你的電話。) –

回答

2

您發佈的(不完整)代碼沒有問題,所以出現錯誤的代碼的其他部分。由於代碼不完整,我必須對發生的事情進行一些教育性的猜測。 (注意:如果您在發佈問題時可以創建complete examples,那麼確實會更好,因此您可以確保引起問題的原因包括在內。)

由於您收到IllegalStateException「不在FX應用程序線程「,您必須從後臺線程更新UI。由於您在後臺線程中運行的唯一代碼位於您在aBigProcessingEvent()中創建的Task中,因此UI更新必須發生在未顯示的一個或多個processingEventXX()方法中。

如果您將電話打包到processingEventXX()Platform.runLater(),那麼您將看不到進度條的任何漸進式更新。 Platform.runLater()安排您提供的在FX應用程序線程上執行的runnable並立即退出。 Task中沒有其他代碼需要花費時間運行,因此整個任務只需很短的時間即可完成,並且在FX應用程序線程呈現下一幀時,任務完成且進度屬性爲1

因此推測你的processingEventXX()方法需要時間來執行,並且還要更新UI。您必須在Platform.runLater(...)中將更新UI的調用包裝在這些方法中。包裝在Platform.runLater(...)中的代碼必須包含需要很長時間才能運行的代碼,而不是而不是。即他們應該看起來像

private void processingEvent01() { 
    // some long-running process here... 

    Platform.runLater(() -> { 
     // update UI here.... 
    }); 

    // some other long-running process here (perhaps) 
} 
+0

感謝和混亂中,詹姆斯遺憾。我不確定是否應該運行代碼來更新工作/任務線程中的UI,因爲它會導致IllegalStateException。因此,我認爲這是我的設計上的缺陷,想知道是否也許我是長流程用工=新任務(){}功能之外運行processingEvent01然後以某種方式讓後臺線程/進度條來監聽更新。我想我將不得不隨身攜帶Platform.runLater。 – AnotherNewbplusplus

+1

閱讀我的答案[這裏](http://stackoverflow.com/questions/30249493/using-threads-to-make-database-requests),看看它是否有幫助。 –

+0

是的,該鏈接無疑幫助我理解了JavaFX多線程的基礎知識,並且使用Platform.runLater()來定位GUI更新的上述答案絕對是我需要的。再次感謝! – AnotherNewbplusplus