来自Task的tableview中的JavaFX更新进度条

     2023-02-21     256

关键词:

【中文标题】来自Task的tableview中的JavaFX更新进度条【英文标题】:JavaFX Update progressbar in tableview from Task 【发布时间】:2013-05-19 06:04:06 【问题描述】:

我知道 Task 有一个方法 updateProgress,我需要将进度条绑定到任务,但是我不能这样做,因为我没有进度条作为对象。

我的程序有一个 TableView。一旦用户输入下载 url 并单击下载在 TableView 中创建的新行。行有一些信息和进度条列。然后我开始一个新线程 - 任务。所有下载都在哪里完成,我需要以某种方式更新该行中的进度条。

我尝试将 SimpleDoubleProperty 绑定到任务,但它没有更新进度条...

【问题讨论】:

【参考方案1】:

James D 在 Oracle JavaFX 论坛帖子中解决了这个问题:Table cell progress indicator。我刚刚将该解决方案复制到此答案中。

该解决方案创建多个任务并通过TableView 中的一组进度条监控它们的进度。

原始线程还包括一个使用ProgressIndicators 的解决方案,以防您更喜欢ProgressBars。

import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator ;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ProgressBarTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class ProgressBarTableCellTest extends Application 

  @Override
  public void start(Stage primaryStage) 
    TableView<TestTask> table = new TableView<TestTask>();
    Random rng = new Random();
    for (int i = 0; i < 20; i++) 
      table.getItems().add(
          new TestTask(rng.nextInt(3000) + 2000, rng.nextInt(30) + 20));
    

    TableColumn<TestTask, String> statusCol = new TableColumn("Status");
    statusCol.setCellValueFactory(new PropertyValueFactory<TestTask, String>(
        "message"));
    statusCol.setPrefWidth(75);

    TableColumn<TestTask, Double> progressCol = new TableColumn("Progress");
    progressCol.setCellValueFactory(new PropertyValueFactory<TestTask, Double>(
        "progress"));
    progressCol
        .setCellFactory(ProgressBarTableCell.<TestTask> forTableColumn());

    table.getColumns().addAll(statusCol, progressCol);

    BorderPane root = new BorderPane();
    root.setCenter(table);
    primaryStage.setScene(new Scene(root));
    primaryStage.show();

    ExecutorService executor = Executors.newFixedThreadPool(table.getItems().size(), new ThreadFactory() 
      @Override
      public Thread newThread(Runnable r) 
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
      
    );


    for (TestTask task : table.getItems()) 
      executor.execute(task);
    
  

  public static void main(String[] args) 
    launch(args);
  

  static class TestTask extends Task<Void> 

    private final int waitTime; // milliseconds
    private final int pauseTime; // milliseconds

    public static final int NUM_ITERATIONS = 100;

    TestTask(int waitTime, int pauseTime) 
      this.waitTime = waitTime;
      this.pauseTime = pauseTime;
    

    @Override
    protected Void call() throws Exception 
      this.updateProgress(ProgressIndicator.INDETERMINATE_PROGRESS, 1);
      this.updateMessage("Waiting...");
      Thread.sleep(waitTime);
      this.updateMessage("Running...");
      for (int i = 0; i < NUM_ITERATIONS; i++) 
        updateProgress((1.0 * i) / NUM_ITERATIONS, 1);
        Thread.sleep(pauseTime);
      
      this.updateMessage("Done");
      this.updateProgress(1, 1);
      return null;
    

  

基于评论问题的说明性文字

仅当您难以理解上述代码的工作原理并希望更深入地了解单元格值和属性连接时,才需要阅读本节。

这里没有任何类型的绑定(至少我没有看到)。

绑定(或 ChangeListener,相当于同一件事)隐藏在 PropertyValueFactory 和 ProgressBarTableCell 的实现后面。我们看一下相关代码:

TableColumn<TestTask, Double> progressCol = new TableColumn("Progress");
progressCol.setCellValueFactory(
  new PropertyValueFactory<TestTask, Double>("progress")
);
progressCol.setCellFactory(
  ProgressBarTableCell.<TestTask> forTableColumn()
);

progressCol 被定义为将 TestTask 作为数据行并从测试任务属性中提取一个双精度值。

单元格值工厂定义如何填充列的双精度值。它是基于采用参数“progress”的 PropertyValueFactory 定义的。这告诉属性值工厂使用 JavaFX 命名约定和 Java 反射 API 来查找相关方法以从 TestTask 实例中检索数据。在这种情况下,它将调用 TestTask 实例上名为 progressProperty() 的方法来检索反映任务进度的 ReadOnlyDoubleProperty。

正如它在文档中所述,PropertyValueFactory 只是下面混乱代码的简写,但关键事实是它返回一个 ObservableValue,Table 实现可以使用该值将单元格的值设置为单元格变化。

TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() 
  public ObservableValue<String> call(CellDataFeatures<Person, String> p) 
    // p.getValue() returns the Person instance for a particular TableView row
    return p.getValue().firstNameProperty();
  
);

好的,所以现在我们有了一个单元格的值,每当任务取得任何进展时,都会将单元格的值反映为任务进度的双精度值。但是我们仍然需要以某种方式以图形方式表示该 double 值。这就是ProgressBarTableCell 所做的。它是一个包含进度条的表格单元格。 forTableColumn 方法创建了一个工厂,该工厂为列中的每个非空行生成 ProgressBarTableCells,并设置进度条的进度以匹配已通过 PropertyValueFactory 链接到任务进度属性的单元格值。

在理解详细实现时感到困惑。 . .当然。但是这些高级助手工厂和单元为您处理了许多低级链接细节,因此您不需要一遍又一遍地编写它们,并且从简单的 API 使用角度来看,它(希望)很简单,并且合乎逻辑。

也没有属性(如 SimpleStringProperty 等),所以问题是,如果我需要更多具有 SimpleStringProperty 的列,我该如何将它们添加到这种 TableView 中?

再次使用 PropertyValueFactory。让我们想象一下,您有一个名为 URL 的字符串属性,然后您可以像这样添加列:

TableColumn<TestTask, Double> urlCol = new TableColumn("URL");
urlCol.setCellValueFactory(
  new PropertyValueFactory<TestTask, Double>("url")
);

注意我们只需要设置单元格值工厂,这是因为该列的默认单元格工厂将返回一个包含标签的单元格,该标签直接显示单元格的字符串值。

现在,为了使上述内容正常工作,我们需要一个 TestTask 方法,它为任务提供一个 url,例如:

final ReadOnlyStringWrapper url = new ReadOnlyStringWrapper();

public TestTask(String url) 
  this.url.set(url);


public ReadOnlyStringProperty urlProperty() 
  return url.getReadOnlyProperty()

请注意,命名约定在这里非常重要,它必须是 urlProperty() 不能是其他任何东西,否则 PropertyValueFactory 将找不到属性值访问器。

注意,出于这些目的,带有 getUrl() 的简单字符串值与属性一样有效,因为 PropertyValueFactory 可以与 getter 和属性方法一起使用。使用属性方法的唯一优点是它允许表值数据根据属性更改事件自动更新,这对于直接的 getter 是不可能的。但是在这里,因为 url 实际上是最终的,并且对于给定的任务不会改变,所以无论是从任务中为这个文件提供 getter 方法还是属性方法都没有区别。

【讨论】:

测试了这个演示应用程序——它做得很好。但是,对我来说,很难理解这段代码。这里没有任何约束(至少我没有看到)。也没有属性(如 SimpleStringProperty 等),所以问题是,如果我需要更多带有 SimpleStringProperty 的列,我该如何将它们添加到这种 TableView 中?提前致谢。 更新答案以根据萨拉斯评论中的问题提供额外信息 非常感谢您的宝贵时间和如此深刻的回答。这对我帮助很大。 urlCol.setCellValueFactory( new PropertyValueFactory&lt;TestTask, Double&gt;("url"),不应该是String而不是Double吗? @jeppla 是的 jeppla,值得指出的是 JavaFX 并发类 TaskService 都继承自 Worker 接口。 Worker 接口公开了一个progress property。所以任何从 Task 或 Service 继承的东西也会暴露 progress 属性。它是监视这些类的进度属性中的更改值,使您可以适当地更新与它们关联的进度条。这是理解示例的关键。

TableView 中的 JavaFX 属性

】TableView中的JavaFX属性【英文标题】:JavaFXPropertiesinTableView【发布时间】:2014-09-1309:46:02【问题描述】:我正在自学如何在TableView中使用JavaFX属性,并且在处理某些属性类型时遇到了问题。我有一个包含两个属性的对象Personpublic... 查看详情

如何从数据库javafx中搜索tableview中的数据[重复]

】如何从数据库javafx中搜索tableview中的数据[重复]【英文标题】:howtosearchdataintableviewfromadatabasejavafx[duplicate]【发布时间】:2019-04-1104:49:33【问题描述】:publicvoidSearch(ActionEventevent)tryConnectionconn=SqliteConnection.Connector();PreparedSta 查看详情

javafx的tableview基本用法(代码片段)

JAVAFX中的表格显示主要使用TableView与TableView相关的类:TableColumnTableRowTableCellTablePositionTableViewFocusModelTableViewSelectionModelJavaFXTableView例子:importjavafx.application.Application;importjavafx.scene.Sce 查看详情

javafx的tableview基本用法(代码片段)

JAVAFX中的表格显示主要使用TableView与TableView相关的类:TableColumnTableRowTableCellTablePositionTableViewFocusModelTableViewSelectionModelJavaFXTableView例子:importjavafx.application.Application;importjavafx.scene.Sce 查看详情

如何在javafx中保存/加载tableview中的数据

我创建了一个TableView,允许用户在表中输入信息。我遇到的问题是我需要保存用户提供的信息,并在下次运行程序时重新加载相同的表,并使用相同的信息。我已经被困了一段时间,如果有人可以提供帮助我会非常感激。以下是... 查看详情

JavaFX TableView 文本对齐

】JavaFXTableView文本对齐【英文标题】:JavaFXTableViewtextalignment【发布时间】:2012-11-0711:09:30【问题描述】:正如您在图片中看到的,每个列的文本对齐方式设置为左对齐。有什么办法可以改变这个吗?到目前为止,我已经在我的CS... 查看详情

使用 tableview (JavaFX) 绑定 hashmap

】使用tableview(JavaFX)绑定hashmap【英文标题】:Bindinghashmapwithtableview(JavaFX)【发布时间】:2013-09-0805:52:18【问题描述】:我想在JavaFXTableview中显示HashMap内容。请在下面找到我用来将HashMap内容设置到表格列中的代码。我遇到的问题... 查看详情

我可以在 Javafx TableView 中对行进行分组吗?

】我可以在JavafxTableView中对行进行分组吗?【英文标题】:CanIgrouprowsinaJavafxTableView?【发布时间】:2019-10-0913:17:21【问题描述】:我想制作一个javafxTableView,其中的行可能(或可能没有)“相关”行。基本上,我认为它可能是tabl... 查看详情

Javafx 8:在初始化方法中填充 TableView

】Javafx8:在初始化方法中填充TableView【英文标题】:Javafx8:PopulatingaTableViewininitializemethod【发布时间】:2017-04-1013:45:49【问题描述】:我是JavaFX8的新手,我正在尝试使用初始化方法为TableView提供控制器中的一些数据。我已经看到... 查看详情

如何使用css来设置两个相邻tableview的边框样式,使它们看起来像是javafx11中的单个tableview(openjfx11)(代码片段)

一个CSS新手问题。我在两个相邻的TableViews中显示了一组广泛的数据,并且双向绑定了它们的ScrollBars,FocusModels和SelectionModels以使它们保持同步。我现在正试图让两个TableViews看起来像一个,并希望:当TableView有焦点时,TableViews周... 查看详情

如何将行和列添加到 JavaFX 8 TableView

】如何将行和列添加到JavaFX8TableView【英文标题】:HowcanIaddrowsandcolumnstoaJavaFX8TableView【发布时间】:2014-10-1305:43:09【问题描述】:我在Internet上看到了向TableView添加行的示例,例如使用Oracledocumentation中的Person类。但我有可变数量... 查看详情

如何根据javafx中的Action为同一个tableview提供一个Contextmenu?

】如何根据javafx中的Action为同一个tableview提供一个Contextmenu?【英文标题】:HowtogiveaContextmenutosametableviewaccordingtoActioninjavafx?【发布时间】:2016-08-1905:08:48【问题描述】:我有一个组合框和一个表格。组合框有两个项目,它们是ty... 查看详情

来自json数组的TableView中的imageView

】来自json数组的TableView中的imageView【英文标题】:imageViewinTableViewfromjsonarray【发布时间】:2017-10-2602:59:05【问题描述】:我正在使用Objective-c开发一个简单的IOS应用程序,我想将图像加载到表格单元格中。我有一个小网络服务,... 查看详情

将javafx中的label更改为不同的值

这不会改变我的标签。只有第一个运行而其他运行不会更改。在Java中它起作用但在Javafx中没有。packagevehicalservice;importcom.jfoenix.controls.JFXProgressBar;importjava.io.IOException;importjava.net.URL;importjava.util.ResourceBundle;importjavaf 查看详情

JavaFX中表格视图单元格中的组合框

...vaFX中表格视图单元格中的组合框【英文标题】:ComboBoxinatableviewcellinJavaFX【发布时间】:2016-05-0922:58:11【问题描述】:我正在尝试将ComboBox添加到我的TableView:基本上我有一个名为TableViewTest的类,它存储名称和描述,我可以在Tab... 查看详情

如何绕过 JavaFX 的 TableView “占位符”?

】如何绕过JavaFX的TableView“占位符”?【英文标题】:HowcanIbypasstheJavaFX\'sTableView"placeholder"?【发布时间】:2013-06-0406:20:26【问题描述】:JavaFX的TableView有一个placeholder属性,它基本上是一个Node,只要它为空就会显示在Table... 查看详情

JavaFX 初始化方法中的 NullPointerException

...我可以将这些数据(电子邮件主题、发件人、日期)放入TableView但......只有当我等待负责在TableView中设置这些数据的线 查看详情

无法将值插入 tableview - JavaFx

】无法将值插入tableview-JavaFx【英文标题】:Unabletoinsertvaluesintotableview-JavaFx【发布时间】:2017-09-2916:35:22【问题描述】:我是JavaFx的新手,我正在尝试创建一个tableview,而fxml是使用场景构建器创建的。如果我运行该程序,则该表... 查看详情