Skip to main content
 首页 » 编程设计

JavaFX:如何在向 TableView 添加列时显示动画 ProgressIndicator

2024年11月24日62mate10pro

在我的应用程序中,将 TableColumn 对象添加到我的 TableView 是一项耗时的操作 — 它会导致所有内容卡住 3-4 秒。我希望在发生这种情况时保持 UI 响应,但这正是 必须 在 JavaFX 应用程序线程上完成的事情。有什么办法吗?

package tableviewpausetest; 
 
import java.util.concurrent.Executors; 
import java.util.concurrent.ExecutorService; 
import javafx.application.Application; 
import javafx.application.Platform; 
import javafx.concurrent.WorkerStateEvent; 
import javafx.concurrent.Task; 
import javafx.event.ActionEvent; 
import javafx.event.EventHandler; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.ProgressIndicator; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.HBox; 
import javafx.stage.Stage; 
 
 
/** 
 * 
 * @author drmc 
 */ 
public class TableViewPauseTest extends Application { 
 
    public static final int ROW_COUNT = 100; 
    public static final int COLUMN_COUNT = 80; 
    public static final ExecutorService executor = Executors.newSingleThreadExecutor(); 
 
    private TableView<String> tableView = new TableView<>(); 
    private Button button = new Button("Toggle Columns Visibility"); 
    private ProgressIndicator progressIndicator = new ProgressIndicator(); 
    private HBox buttonBox = new HBox(8); 
    private BorderPane borderPane = new BorderPane(); 
    private Task task = null; 
 
    public void start(Stage primaryStage) { 
 
        this.tableView.setColumnResizePolicy( 
            TableView.CONSTRAINED_RESIZE_POLICY); 
        for (int i = 0; i < ROW_COUNT; ++i) { 
            this.tableView.getItems().add(":)"); 
        } 
 
        this.button.setOnAction(new ToggleHandler(this)); 
        this.buttonBox.getChildren().setAll(this.button); 
 
        this.borderPane.setCenter(this.tableView); 
        this.borderPane.setBottom(this.buttonBox); 
 
        Scene scene = new Scene(this.borderPane, 1024, 768); 
        primaryStage.setTitle("tableviewpausetest"); 
        primaryStage.setScene(scene); 
        primaryStage.show(); 
    } 
 
    private class ToggleHandler implements EventHandler<ActionEvent> { 
 
        private TableViewPauseTest app; 
 
        public ToggleHandler(TableViewPauseTest app) { 
            this.app = app; 
        } 
 
        @Override 
        public void handle(ActionEvent event) { 
            // Show the progress indicator. 
            this.app.buttonBox.getChildren().add(this.app.progressIndicator); 
            this.app.progressIndicator.setPrefHeight(this.app.button.getHeight()); 
 
            // Ensure the columns exist. 
            if (this.app.tableView.getColumns().isEmpty()) { 
                for (int i = 0; i < COLUMN_COUNT; ++i) { 
                    TableColumn<String, String> tableColumn = new TableColumn<>( 
                        String.format("%s", i)); 
                    tableColumn.setVisible(false); 
                    this.app.tableView.getColumns().add(tableColumn); 
                } 
            } 
 
            // Create and submit a concurrent task to toggle column visibility. 
            this.app.task = new ToggleTask(this.app); 
            this.app.task.setOnSucceeded(new ToggleSucceededHandler(this.app)); 
            executor.submit(this.app.task); 
        } 
 
    } 
 
    private class ToggleSucceededHandler implements EventHandler<WorkerStateEvent> { 
 
        private TableViewPauseTest app; 
 
        public ToggleSucceededHandler(TableViewPauseTest app) { 
            this.app = app; 
        } 
 
        @Override 
        public void handle(WorkerStateEvent event) { 
            // Hide the progress indicator. 
            this.app.buttonBox.getChildren().remove(this.app.progressIndicator); 
        } 
 
    } 
 
    private class ToggleTask extends Task<String> { 
 
        private TableViewPauseTest app; 
 
        public ToggleTask(TableViewPauseTest app) { 
            this.app = app; 
        } 
 
        @Override 
        public String call() { 
            boolean newState = false; 
            String action = "hide"; 
            if (this.app.tableView.getVisibleLeafColumns().isEmpty()) { 
                newState = true; 
                action = "show"; 
            } 
 
            // This action must be performed on the JavaFX Application Thread, 
            // and it causes an extremely uncomfortable pause in my application. 
            Platform.runLater(new ToggleRunnable(this.app.tableView, newState)); 
 
            return action; 
        } 
 
    } 
 
    private class ToggleRunnable implements Runnable { 
 
        private TableView<?> tableView; 
        private boolean newState; 
 
        public ToggleRunnable(TableView<?> tableView, boolean newState) { 
            this.tableView = tableView; 
            this.newState = newState; 
        } 
 
        @Override 
        public void run() { 
            for (TableColumn<?, ?> tableColumn : this.tableView.getColumns()) { 
                tableColumn.setVisible(this.newState); 
            } 
        } 
 
    } 
 
    public static void main(String[] args) { 
        Application.launch(args); 
    } 
 
} 

请您参考如下方法:

我认为这个示例代码对您有用

/* 
 * To change this template, choose Tools | Templates 
 * and open the template in the editor. 
 */ 
package progressbartablecelltest; 
 
import java.util.Random; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.ThreadFactory; 
import javafx.application.Application; 
import javafx.beans.value.ObservableValue; 
import javafx.concurrent.Task; 
import javafx.scene.Scene; 
import javafx.scene.control.ProgressIndicator; 
import javafx.scene.control.TableCell; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.control.cell.PropertyValueFactory; 
import javafx.scene.layout.BorderPane; 
import javafx.stage.Stage; 
import javafx.util.Callback; 
 
/** 
 * 
 * @author reegan 
 */ 
public class ProgressBarTableCellTest 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); 
    } 
  } 
} 

Table Column Add with Progress Indicator