在 JavaFX table 分页中添加进度条

Add progress bar in JavaFX table pagination

我有这个JavaFX的例子table。

    import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Pagination;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class MainApp extends Application
{

    private final static int dataSize = 10_023;
    private final static int rowsPerPage = 1000;

    private final TableView<Sample> table = createTable();
    private final List<Sample> data = createData();

    private List<Sample> createData()
    {
        List<Sample> data = new ArrayList<>(dataSize);

        for (int i = 0; i < dataSize; i++)
        {
            data.add(new Sample(i, "foo " + i, "bar " + i));
        }

        return data;
    }

    private TableView<Sample> createTable()
    {

        TableView<Sample> table = new TableView<>();

        TableColumn<Sample, Integer> column1 = new TableColumn<>("Id");
        column1.setCellValueFactory(param -> param.getValue().id);
        column1.setPrefWidth(150);

        TableColumn<Sample, String> column2 = new TableColumn<>("Foo");
        column2.setCellValueFactory(param -> param.getValue().foo);
        column2.setPrefWidth(250);

        TableColumn<Sample, String> column3 = new TableColumn<>("Bar");
        column3.setCellValueFactory(param -> param.getValue().bar);
        column3.setPrefWidth(250);

        table.getColumns().addAll(column1, column2, column3);

        return table;
    }

    private Node createPage(int pageIndex)
    {

        int fromIndex = pageIndex * rowsPerPage;
        int toIndex = Math.min(fromIndex + rowsPerPage, data.size());
        table.setItems(FXCollections.observableArrayList(data.subList(fromIndex, toIndex)));

        return new BorderPane(table);
    }

    @Override
    public void start(final Stage stage) throws Exception
    {

        Pagination pagination = new Pagination((data.size() / rowsPerPage + 1), 0);
        pagination.setPageFactory(this::createPage);

        Scene scene = new Scene(new BorderPane(pagination), 1024, 768);
        stage.setScene(scene);
        stage.setTitle("Table pager");
        stage.show();
    }

    public static void main(String[] args) throws Exception
    {
        launch(args);
    }

    public static class Sample
    {

        private final ObservableValue<Integer> id;
        private final SimpleStringProperty foo;
        private final SimpleStringProperty bar;

        private Sample(int id, String foo, String bar)
        {
            this.id = new SimpleObjectProperty<>(id);
            this.foo = new SimpleStringProperty(foo);
            this.bar = new SimpleStringProperty(bar);
        }
    }
}

有没有办法在页面切换和加载数据时添加进度条?

我不太清楚如何在加载数据时使用进度指示器。

有什么有用的例子吗?

可能最简单的解决方案是重写 createPage(int) 方法:

  1. 替换placeholder of the TableView with a ProgressIndicator.
  2. 创建一个 Task 来计算下一页的内容(在我的例子中只是一个虚拟人在做 Thread.sleep())。
  3. Task 完成后,将占位符替换为旧的(备份和恢复)并将数据设置为 TableView

这是 createPage() 方法的一个小例子:

private Node createPage(int pageIndex) {
    Node oldPlaceHolder = table.getPlaceholder();

    ProgressIndicator progress = new ProgressIndicator();
    progress.setMaxSize(90, 90);
    table.setPlaceholder(new StackPane(progress));
    table.getItems().clear();

    Task<ObservableList<Sample>> task = new Task<ObservableList<Sample>>() {

        @Override
        protected ObservableList<Sample> call() throws Exception {
            for(int i = 0; i < 99; i++)
            {
                Thread.sleep(20);
                updateProgress(i, 100);
            }
            int fromIndex = pageIndex * rowsPerPage;
            int toIndex = Math.min(fromIndex + rowsPerPage, data.size());
            return (FXCollections.observableArrayList(data.subList(
                    fromIndex, toIndex)));
        }
    };

    progress.progressProperty().bind(task.progressProperty());
    task.setOnSucceeded(ev -> {
        table.setItems(task.getValue());
        table.setPlaceholder(oldPlaceHolder);
    });
    new Thread(task).start();

    return new BorderPane(table);
}

您可以将 table 放入 StackPane 并在其顶部放置一个进度指示器。

这是一个例子:

public class TableWithProgressIndicator extends Application {

    private final static int dataSize = 10_023;
    private final static int rowsPerPage = 1000;

    private ObservableList<Sample> dataList = FXCollections.observableArrayList(new ArrayList<Sample>(rowsPerPage));
    private TableView<Sample> table = null;


    StackPane tablePane = new StackPane();
    ProgressIndicator progressIndicator = new ProgressIndicator();

    private List<Sample> loadData(int fromIndex, int toIndex) {

        List<Sample> list = new ArrayList<>();

        try {

            for (int i = fromIndex; i < toIndex; i++) {
                list.add(new Sample(i, "foo " + i, "bar " + i));
            }

            Thread.sleep(2000);

        } catch( Exception e) {
            e.printStackTrace();
        }

        return list;
    }


    @Override
    public void start(final Stage stage) throws Exception {

        table = createTable( dataList);

        Pagination pagination = new Pagination((dataSize / rowsPerPage + 1), 0);
        progressIndicator.setMaxSize(200, 200);

        // wrap table and progress indicator into a stackpane, progress indicator is on top of table
        tablePane.getChildren().add(table);
        tablePane.getChildren().add(progressIndicator);

        pagination.setPageFactory(new Callback<Integer, Node>() {

            public Node call(final Integer pageIndex) {

                progressIndicator.setVisible(true);

                dataList.clear();

                // long running background task
                new Thread() {
                    public void run() {

                        try {

                            int fromIndex = pageIndex * rowsPerPage;
                            int toIndex = Math.min(fromIndex + rowsPerPage, dataSize);

                            List<Sample> loadedList = loadData(fromIndex, toIndex);

                            Platform.runLater(() -> dataList.setAll( loadedList));

                        } finally {

                            Platform.runLater(() -> progressIndicator.setVisible(false));

                        }
                    }
                }.start();

                return tablePane;
            }
        });

        Scene scene = new Scene(new BorderPane(pagination), 1024, 768);
        stage.setScene(scene);
        stage.setTitle("Table pager");
        stage.show();
    }

    private TableView<Sample> createTable( ObservableList<Sample> data) {

        TableView<Sample> table = new TableView<>();

        TableColumn<Sample, Integer> column1 = new TableColumn<>("Id");
        column1.setCellValueFactory(param -> param.getValue().id);
        column1.setPrefWidth(150);

        TableColumn<Sample, String> column2 = new TableColumn<>("Foo");
        column2.setCellValueFactory(param -> param.getValue().foo);
        column2.setPrefWidth(250);

        TableColumn<Sample, String> column3 = new TableColumn<>("Bar");
        column3.setCellValueFactory(param -> param.getValue().bar);
        column3.setPrefWidth(250);

        table.getColumns().addAll(column1, column2, column3);

        table.setItems( data);

        table.setPlaceholder(new Label(""));

        return table;
    }

    public static void main(String[] args) throws Exception {
        launch(args);
    }

    public static class Sample {

        private final ObservableValue<Integer> id;
        private final SimpleStringProperty foo;
        private final SimpleStringProperty bar;

        private Sample(int id, String foo, String bar) {
            this.id = new SimpleObjectProperty<>(id);
            this.foo = new SimpleStringProperty(foo);
            this.bar = new SimpleStringProperty(bar);
        }
    }
}