2017-05-09 158 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