繁体   English   中英

如何在 TableView 的每一行中放置一个 ProcessIndicator 并指示任务状态

[英]How to put a ProcessIndicator in each row of a TableView and indicate task status

我正在尝试在 JavaFX 中执行以下操作:

  • 有一个包含多行的TableView
  • 每行包含带有文本的列和一个进度/状态列。
  • 当按下一个特定的Button时,对于TableView的每一行,应该执行一些任务,一行接一行。 (例如检查一些数据,...)
  • 执行此任务时,Status 列中应显示一个不确定的ProgressIndicator ,直到该行的任务完成,然后指示器显示为已完成。
  • 当每一行的所有任务都完成后,可以再次按下按钮来重置状态并再次执行任务。

我在这篇相关的 Stackoverflow 帖子此处找到了一些帮助,并尝试根据需要进行调整,但遇到了一些问题:

  1. 目前,当我运行程序时,每行的每个ProgressIndicator都会立即显示(不确定)。 一旦按下按钮,我怎样才能只激活它们/使它们逐行可见?
  2. 假任务完成后再次按下按钮不会重新启动它。 我将如何修改/重建程序以使重置成为可能?
  3. 总体方法是否有意义?

我当前的可运行代码:

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

import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
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 < 3; i++) {
            table.getItems().add(new TestTask(rng.nextInt(3000) + 2000, "Test"));
        }

        TableColumn<TestTask, String> nameCol = new TableColumn("Name");
        nameCol.setCellValueFactory(new PropertyValueFactory<TestTask, String>("name"));
        nameCol.setPrefWidth(75);

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

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

        BorderPane root = new BorderPane();
        root.setCenter(table);
        Button btn = new Button("Start");
        btn.setOnAction(actionEvent -> {
            ExecutorService executor = Executors.newSingleThreadExecutor();

            for (TestTask task : table.getItems()) {
                executor.submit(task);
            }
        });

        root.setBottom(btn);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();

    }

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

    public static class TestTask extends Task<Void> {
        private final int waitTime; // milliseconds
        final ReadOnlyStringWrapper name = new ReadOnlyStringWrapper();
        public static final int NUM_ITERATIONS = 100;

        public TestTask(int waitTime, String name) {
            this.waitTime = waitTime;
            this.name.set(name);
        }

        public ReadOnlyStringProperty nameProperty() {
            return name.getReadOnlyProperty();
        }

        @Override
        protected Void call() throws Exception {
            this.updateProgress(ProgressIndicator.INDETERMINATE_PROGRESS, 1);
            Thread.sleep(waitTime);
            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.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);
        }
    }
}

而现在的output:

在此处输入图像描述 在此处输入图像描述

这是实现您的第一个问题的版本。 有了这个要求,单元格只是任务的 state 中的一个 function。如果它是RUNNING ,显示一个不确定的进度指示器; 如果成功则显示值为 1 的SUCCEEDED指示器; 否则,什么也不显示。

请注意,原始问题非常陈旧,并且使用了很多过时的代码 styles。我已相应更新。

import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

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

public class ProgressIndicatorTableCellTest extends Application {
    public void start(Stage primaryStage) {
        TableView<TestTask> table = new TableView<>();
        Random rng = new Random();
        for (int i = 0; i < 3; i++) {
            table.getItems().add(new TestTask(rng.nextInt(3000) + 2000, "Test"));
        }

        TableColumn<TestTask, String> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(data -> data.getValue().nameProperty());
        nameCol.setPrefWidth(75);

        TableColumn<TestTask, Worker.State> progressCol = new TableColumn<>("Progress");
        progressCol.setCellValueFactory(data -> data.getValue().stateProperty());
        progressCol.setCellFactory(col -> new ProgressIndicatorTableCell<>());

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

        BorderPane root = new BorderPane();
        root.setCenter(table);
        Button btn = new Button("Start");
        btn.setOnAction(actionEvent -> {
            ExecutorService executor = Executors.newSingleThreadExecutor(r -> {
                Thread t = new Thread(r);
                t.setDaemon(true);
                return t;
            });

            for (TestTask task : table.getItems()) {
                executor.submit(task);
            }
        });

        root.setBottom(btn);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();

    }

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

    public static class TestTask extends Task<Void> {
        private final int waitTime; // milliseconds
        final ReadOnlyStringWrapper name = new ReadOnlyStringWrapper();
        public static final int NUM_ITERATIONS = 100;

        public TestTask(int waitTime, String name) {
            this.waitTime = waitTime;
            this.name.set(name);
        }

        public ReadOnlyStringProperty nameProperty() {
            return name.getReadOnlyProperty();
        }

        @Override
        protected Void call() throws Exception {
            this.updateProgress(ProgressIndicator.INDETERMINATE_PROGRESS, 1);
            Thread.sleep(waitTime);
            this.updateProgress(1, 1);
            return null;
        }
    }
}

class ProgressIndicatorTableCell<S> extends TableCell<S, Worker.State> {

    private final ProgressIndicator progressIndicator = new ProgressIndicator();

    @Override
    protected void updateItem(Worker.State state, boolean empty) {
        super.updateItem(state, empty);
        if (state == Worker.State.SUCCEEDED) {
            progressIndicator.setProgress(1);
            setGraphic(progressIndicator);
        } else if (state == Worker.State.RUNNING) {
            progressIndicator.setProgress(-1);
            setGraphic(progressIndicator);
        } else {
            setGraphic(null);
        }
    }

}

要允许“重新启动”,您应该使用Service而不仅仅是Task 如果多次按下按钮,此版本将允许重新启动,在继续之前将所有内容返回到初始 state。

此版本还将处理工作从 model class 中分解出来,这对于将职责正确分配给类是可取的:

商品 java:

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleObjectProperty;

public class Item {

    public enum State {WAITING, PROCESSING, READY}

    final ReadOnlyStringWrapper name = new ReadOnlyStringWrapper();

    private final ObjectProperty<State> state = new SimpleObjectProperty<>(State.WAITING);


    public Item(String name) {
        this.name.set(name);
    }

    public ReadOnlyStringProperty nameProperty() {
        return name.getReadOnlyProperty();
    }

    public State getState() {
        return state.get();
    }

    public ObjectProperty<State> stateProperty() {
        return state;
    }

    public void setState(State state) {
        this.state.set(state);
    }
}

进程管理器.java:

import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;

import java.util.List;
import java.util.Random;

public class ProcessManager {

    private final List<Item> items;

    private Random rng = new Random();

    private Service<Void> service = new Service<>() {

        @Override
        protected Task<Void> createTask() {
            return new Task<>() {
                @Override
                protected Void call() throws Exception {
                    for (Item task: items) {
                        try {
                            Platform.runLater(() -> task.setState(Item.State.PROCESSING));
                            Thread.sleep(2000 + rng.nextInt(3000));
                            Platform.runLater(() -> task.setState(Item.State.READY));
                        } catch (InterruptedException exc) {
                            Thread.currentThread().interrupt();
                        }
                        if (isCancelled()) {
                            Platform.runLater(() -> task.setState(Item.State.WAITING));
                            break;
                        }
                    }
                    return null;
                }
            };
        }
    };


    public ProcessManager(List<Item> items) {
        this.items = items ;
        service.setOnCancelled(e -> items.forEach(task -> task.setState(Item.State.WAITING)));
    }


    public void process() {
        service.restart();
    }
}

和应用程序:

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class ProgressIndicatorTableCellTest extends Application {

    public void start(Stage primaryStage) {

        ObservableList<Item> tasks = FXCollections.observableArrayList();

        ProcessManager processManager = new ProcessManager(tasks);

        TableView<Item> table = new TableView<>();
        for (int i = 0; i < 3; i++) {
            Item task = new Item("Item " + (i + 1));
            tasks.add(task);
        }
        table.setItems(tasks);

        TableColumn<Item, String> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(data -> data.getValue().nameProperty());
        nameCol.setPrefWidth(75);

        TableColumn<Item, Item.State> progressCol = new TableColumn<>("Progress");
        progressCol.setCellValueFactory(data -> data.getValue().stateProperty());
        progressCol.setCellFactory(col -> new TableCell<>() {
            private final ProgressIndicator indicator = new ProgressIndicator();
            @Override
            protected void updateItem(Item.State state, boolean empty) {
                super.updateItem(state, empty);
                if (state == Item.State.PROCESSING) {
                    indicator.setProgress(-1);
                    setGraphic(indicator);
                } else if (state == Item.State.READY) {
                    indicator.setProgress(1);
                    setGraphic(indicator);
                } else {
                    setGraphic(null);
                }
            }
        });

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

        BorderPane root = new BorderPane();
        root.setCenter(table);
        Button btn = new Button("Start");
        btn.setOnAction(actionEvent -> processManager.process());

        root.setBottom(btn);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();

    }

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

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM