2017-05-09 155 views
1

我正在設計Javafx中的多線程應用程序,並希望爲每個線程的名稱和進度添加一個帶有列的TableView。做大量的研究後,我發現了什麼,我想在這裏完成一個類似的例子:從線程多次更新JavaFX ProgressIndicator

JavaFX Update progressbar in tableview from Task

(指向這一點:「https://community.oracle.com/message/10999916」)

我遇到,但問題,在這個例子中很好地說明了;你怎麼能多次調用一個'Task'對象來更新一個ProgressIndicator?

我從Oracle的文檔中瞭解到,任務對象「是一次性類,不能重用」。這似乎只能調用Task對象的call()方法一次。我需要多次更新任務,因爲它通過一個Thread類進行多次,而不是一次調用它並通過For循環任意遞增。

我已閱讀關於綁定到監聽器和創建服務類的信息,但我不確定這些是否是針對此問題的實際解決方案。因此,我想問一下在Javafx中這是否可行,或者我可能忽略了一些東西。如果有人已經完成了這個過程,如果你能夠說明如何通過前面提供的例子,這將是非常有幫助的。

任何方向對此將不勝感激,謝謝。

-DREW

編輯1:我修改了措辭,因爲它是不準確的。

編輯2:這裏是一些僞代碼的例子。說我有下面的代碼類:

public static class TaskEx extends Task<Void>{ 

    @Override 
    protected Void call(){ 

    updateProgress(.5, 1); 

    return null 

} 

public static void callThread() { 
    TableView<TaskEx> table = new TableView<TaskEx>(); 
    //Some code for data in table. 

    TableColumn progressColumn = new TableColumn ("Progress"); 
    progressColumn.setCellValueFactory(new PropertyValueFactor("progress"); 

    table.setItems(<data>); 
    table.getColumns();addAll(progressColumn); 

    ExecutorService executor = Executors.newFixedThreadPool(<SomeNumber>); 

    for(TaskEx task : table.getItems(){ 
    Threading.ThreadClass newThread = new Threading.ThreadClass(task); 
    executor.submit(newThread, <uniqueID>); 
    } 
} 

然後說我有一個第二類與此邏輯線程:

static class ThreadClass extends Thread{ 
    Task progressTask; 

    public ThreadClass(Task task, Integer id){ 
    progressTask = task; 
    } 

public void run(){ 
    ExecutorService executor = Executors.newFixedThreadPool(<someNumber>); 

    //This invokes the Task call for the correct progressIndicator in the Tableview. 
    //It will correctly set the progressIndicator to 50% done. 
    executor.submit(progressTask); 

    /* Main logic of the Threading class that involves the 'id' passed in. */ 

    //This will do nothing because you cannot invoke the Task call more than once. 
    executor.submit(progressTask); 
    } 
} 

這是哪門子的工作流程,我需要的,但我不確定如何做到這一點。

+2

*「這似乎那時,人們只能調用任務對象‘的UpdateProgress’一次。」 *這顯然是不正確的:比如你掛電話'的UpdateProgress(...)'在一個循環,所以每個任務都會多次調用它。所有的文檔都意味着你只能調用Task的'call()'方法一次。您可以在'call()'方法內多次調用'updateProgress'。你鏈接的例子似乎是你描述你想要完成的完整實現。你能解釋一下你的問題有什麼不同嗎? –

+2

嘿詹姆斯。我確信我說得那麼糟糕。我的意思是,爲了正確地增加任務的進度,我需要多次調用Task的call()方法。我知道我可以循環並在調用update()方法後繼續調用updateProgress,但我不知道當我調用它時線程將花費多長時間完成。我曾計劃在線程邏輯中的某個特定位置調用Task的call()方法一次,然後再次調用該方法,等等。 –

+1

我想你一定誤解了「任務」的目的?該任務代表「你正在做的事情」 - 所以它應該是你感興趣的任務的進展。如果情況並非如此,那麼您使用「Task」作爲什麼?你可以張貼一些代碼來說明問題嗎? –

回答

1

看來你沒有得到我們正在談論的內容。您正在嘗試執行Thread.run()中的邏輯,然後每個線程都會創建一個Task,以便更新進度。

你需要的是將你的邏輯從Thread.run()轉移到Task.call()。你的線程實際上只是一個線程,它所做的只是運行一個Runnable對象(它是Task)。

public class TaskEx extends Task<Void> { 
    @Override 
    protected Void call() { 
     // Do whatever you need this thread to do 
     updateProgress(0.5, 1); 

     // Do the rest 
     updateProgress(1, 1); 
    } 
} 


public static void callThread() { 
    TableView<TaskEx> table = new TableView<TaskEx>(); 
    ObservableList<TaskEx> data = FXCollections.observableArrayList<>(); 
    data.add(new TaskEx()); // Add the data you need 

    TableColumn progressColumn = new TableColumn("Progress"); 
    progressColumn.setCellValueFactory(new PropertyValueFactory("progress")); 
    progressColumn.setCellFactory(column -> { 
     return new TableCell<TaskEx, Double> { 
      private final ProgressBar bp = new ProgressBar(); 

      @Override 
      public void updateItem(Double item, boolean empty) { 
       super.updateItem(item, empty); 

       if (empty || item == null) { 
        setText(null); 
        setGraphic(null); 
       } 
       else { 
        bp.setProgress(item.doubleValue()); 
        setGraphic(bp); 
       } 
      } 
     } 
    }); 

    table.setItems(data); 
    table.getColumns().add(progressColumn); 

    ExecutorService executor = Executors.newFixedThreadPool(data.size()); 

    for (TaskEx task : table.getItems()) { 
     executor.submit(task); 
    } 
} 

這個工具移除了ThreadClass因爲不應該有,必須在一個線程子類來完成任何邏輯。如果您確實需要作爲邏輯的一部分訪問線程對象,請致電TaskEx.call()中的Thread.getCurrentThread()

這個工具也打開多個線程做同樣的事情(這是毫無意義的)。如果您需要執行一組不同的邏輯,則可以創建一組不同的Task子類,或者添加一個構造函數,參加Runnable對象TaskEx

E.g.

public class TaskEx extends Task<Void> { 
    private final Runnable[] logics; 

    public TaskEx(Runnable[] logics) { 
     this.logics = logics; 
    } 

    @Override 
    protected Void call() { 
     for (int i = 0; i < logics.length; i++) { 
      logics[i].run(); 
      updateProgress(i, logics.length); 
     } 
    } 
} 
+0

謝謝Jai!你和James_D幫助我更好地理解'任務'及其目的,儘管我承認有很長的路要走:) –

+0

@DrewB學習是一個持續的過程。我收到了來自James_D,jewelsea和DVarga等專家的許多指導(直接和間接):) – Jai