Skip to Main Content

Java SE (Java Platform, Standard Edition)

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

Table cell progress indicator

JGagnonMay 2 2013 — edited May 22 2013
I see in the JavaFX 2.2 API that there is a table cell implementation for rendering a progress bar. However, it appears that this only supports the progress indicator that takes an incremental update value (as a double, presumably a value between 0 and 1?) to adjust the "progress" in the indicator. There does not appear to be any support for an indeterminate progress indicator that can be embedded in a table cell.

I am asking because I have a situation where there will be a background task that will be launched potentially for any row in a table. The task may take some time to execute and complete and I would like to show an indeterminate progress indicator that will "spin" until the task completes. I wanted to be able to include this as a cell (column) for each row so that individual rows could indicate whether they were currently "working" or done.

Is there, or will there be in the near future, support for this type of functionality? Or, are there suggestions on a way to cobble together something that can do this?

Comments

James_D
If the value in the cell is negative, then the ProgressBar will be displayed as indeterminate.

Otherwise you could implement a custom cell factory and set a ProgressBar as the indicator.

Here's an example of the first technique:
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;
    }

  }
}
JGagnon
Pretty slick. This works great for a progress bar (both indeterminate and otherwise). How could I do the same thing with a progress indicator?
jsmith
Copy and paste the ProgressBarTableCell code and search and replace ProgressBar with ProgressIndicator on it.

http://hg.openjdk.java.net/openjfx/8/master/rt/file/tip/javafx-ui-controls/src/javafx/scene/control/cell/ProgressBarTableCell.java

Here's a minor update to the great example James put together which does this.

Seems to work fine. Only issue I see when running it on a recent jdk8 version is that the indeterminate progress indicator is a bit too big, then the whole indicator shrinks a little bit once the indicator switches to reporting percentage progress. I didn't investigate that any further, likely it's a minor styling bug in the new JavaFX modena style sheet for Java 8 which you could work around by applying your own style to indeterminate progress indicators.
/*
 * Portions of this code (ProgressIndicatorTableCell) are base upon Oracle
 * JavaFX code (ProgressBarTableCell), which is subject to the following license term.
 * 
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package test;

import java.util.Random;
import java.util.concurrent.*;

import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;

public class ProgressIndicatorTableCellTest extends Application {
  public void start(Stage primaryStage) {
    TableView<TestTask> table = new TableView<>();
    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(ProgressIndicatorTableCell.<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;
    }
  }
}

class ProgressIndicatorTableCell<S> extends TableCell<S, Double> {
  public static <S> Callback<TableColumn<S, Double>, TableCell<S, Double>> forTableColumn() {
    return new Callback<TableColumn<S, Double>, TableCell<S, Double>>() {
      @Override
      public TableCell<S, Double> call(TableColumn<S, Double> param) {
        return new ProgressIndicatorTableCell<>();
      }
    };
  }

  private final ProgressIndicator progressIndicator;
  private ObservableValue observable;

  public ProgressIndicatorTableCell() {
    this.getStyleClass().add("progress-indicator-table-cell");

    this.progressIndicator = new ProgressIndicator();
    setGraphic(progressIndicator);
  }

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

    if (empty) {
      setGraphic(null);
    } else {
      progressIndicator.progressProperty().unbind();

      observable = getTableColumn().getCellObservableValue(getIndex());
      if (observable != null) {
        progressIndicator.progressProperty().bind(observable);
      } else {
        progressIndicator.setProgress(item);
      }

      setGraphic(progressIndicator);
    }
  }
}
1 - 3
Locked Post
New comments cannot be posted to this locked post.

Post Details

Locked on May 30 2013
Added on May 2 2013
3 comments
5,273 views