我的回答中有一些文字,因为从您的问题中我不清楚您的实例出了什么问题。希望答案中的解释或示例代码有用。
我可能是错的,但我认为这是 JavaFX UI 控件设计中的一个错误。将进度更新为 0 应重置进度指示器。
你有点误会了。您已将指标的进度绑定到任务的进度。任务已完成,进度为 1。现在,如果您想在另一个任务中重复使用相同的指示器或让它测量其他任务的进度,您必须首先阻止它测量原始任务的进度。要取消原始任务的进度指示器,unbind http://docs.oracle.com/javafx/2/api/javafx/beans/property/Property.html#unbind%28%29这是进步。一旦进度指示器的进度不再与原始任务的进度绑定,您就可以自由地将指示器设置为您想要的任何值,或将其绑定到其他值。
同样,您一次只能将进度指示器的进度绑定到一件事(除非您双向绑定指示器,而您不能对任务进度执行此操作,因为任务进度是只读的并且双向绑定到多个任务进度无论如何,值都会不正确,因为每个任务都会处于不同的进度点)。
使表盘重新出现。
根据您的描述,我不确定为什么表盘会首先消失,然后需要重新出现。通常,当进度指示器的进度达到 1 时,它仍然保持可见,报告完全完成的进度,它不会自动消失。您可能将指示器的可见性设置为 false 或将其不透明度修改为零。这两个属性与指标测量的实际进度无关。或者您可能正在从显示的场景中删除指示器。如果您在任务完成后修改可见性并将指示器设置为不可见,并且您希望随后再次看到它以测量另一个任务的进度,那么您将需要确保它位于场景中,不透明度 > 0 并且可见性设置为 true。
一条建议
您只能运行一个任务一次,因此在完成后,如果它已经取得了一些进展,则将其进度设置回零没有多大意义。
物业类型
A 进度指示器的进度属性 http://docs.oracle.com/javafx/2/api/javafx/scene/control/ProgressIndicator.html#progressProperty%28%29是一个平原DoubleProperty
, not a ReadOnlyDoubleProperty
,所以它是可以直接设置的(只要它没有绑定到另一个值)。
A 任务的进度属性 http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html#progressProperty%28%29这是只读的,必须通过更改更新进度 http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html#updateProgress%28double,%20double%29。任务的进度属性可能被设置为只读,以便可以通过特殊代码确保对其更新是线程安全的updateProgress
常规。
示例代码
考虑以下代码(我相信)它实现了您想要做的事情的意图。该代码模拟了铁人三项赛的运行,其中铁人三项赛的每个阶段(游泳、自行车、跑步)都是一个单独的任务。当铁人三项赛进行时,进度指示器会显示铁人三项赛每个阶段的进度。当铁人三项比赛完成时,进度指示器会逐渐消失,直到新的铁人三项比赛开始。抱歉,示例太长了,我发现很难想出更简洁的内容。
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.beans.*;
import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.concurrent.Task;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Triathlon extends Application {
private final Random random = new Random();
private final ExecutorService exec = Executors.newSingleThreadExecutor();
@Override public void start(Stage stage) throws Exception {
final TaskMonitor taskMonitor = new TaskMonitor();
final ProgressIndicator progressIndicator = new ProgressIndicator();
progressIndicator.progressProperty().bind(
taskMonitor.currentTaskProgressProperty()
);
final Label currentRaceStage = new Label();
currentRaceStage.textProperty().bind(
taskMonitor.currentTaskNameProperty()
);
createMainLayout(
stage,
createStartRaceButton(
exec,
taskMonitor
),
createRaceProgressView(
taskMonitor,
progressIndicator,
currentRaceStage
)
);
}
@Override public void stop() throws Exception {
exec.shutdownNow();
}
private Button createStartRaceButton(final ExecutorService exec, final TaskMonitor taskMonitor) {
final Button startButton = new Button("Start Race");
startButton.disableProperty().bind(taskMonitor.idleProperty().not());
startButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
runRace(exec, taskMonitor);
}
});
return startButton;
}
private HBox createRaceProgressView(final TaskMonitor taskMonitor, ProgressIndicator progressIndicator, Label currentRaceStage) {
final HBox raceProgress = new HBox(10);
raceProgress.getChildren().setAll(
currentRaceStage,
progressIndicator
);
raceProgress.setOpacity(0);
raceProgress.setAlignment(Pos.CENTER);
final FadeTransition fade = new FadeTransition(Duration.seconds(0.75), raceProgress);
fade.setToValue(0);
taskMonitor.idleProperty().addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
if (taskMonitor.idleProperty().get()) {
fade.playFromStart();
} else {
fade.stop();
raceProgress.setOpacity(1);
}
}
});
return raceProgress;
}
private void createMainLayout(Stage stage, Button startButton, HBox raceProgress) {
final VBox layout = new VBox(10);
layout.getChildren().setAll(
raceProgress,
startButton
);
layout.setAlignment(Pos.CENTER);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10px;");
stage.setScene(new Scene(layout, 200, 130));
stage.show();
}
private void runRace(ExecutorService exec, TaskMonitor taskMonitor) {
StageTask swimTask = new StageTask("Swim", 30, 40);
StageTask bikeTask = new StageTask("Bike", 210, 230);
StageTask runTask = new StageTask("Run", 120, 140);
taskMonitor.monitor(swimTask, bikeTask, runTask);
exec.execute(swimTask);
exec.execute(bikeTask);
exec.execute(runTask);
}
class TaskMonitor {
final private ReadOnlyObjectWrapper<StageTask> currentTask = new ReadOnlyObjectWrapper<>();
final private ReadOnlyStringWrapper currentTaskName = new ReadOnlyStringWrapper();
final private ReadOnlyDoubleWrapper currentTaskProgress = new ReadOnlyDoubleWrapper();
final private ReadOnlyBooleanWrapper idle = new ReadOnlyBooleanWrapper(true);
public void monitor(final StageTask task) {
task.stateProperty().addListener(new ChangeListener<Task.State>() {
@Override
public void changed(ObservableValue<? extends Task.State> observableValue, Task.State oldState, Task.State state) {
switch (state) {
case RUNNING:
currentTask.set(task);
currentTaskProgress.unbind();
currentTaskProgress.set(task.progressProperty().get());
currentTaskProgress.bind(task.progressProperty());
currentTaskName.set(task.nameProperty().get());
idle.set(false);
break;
case SUCCEEDED:
case CANCELLED:
case FAILED:
task.stateProperty().removeListener(this);
idle.set(true);
break;
}
}
});
}
public void monitor(final StageTask... tasks) {
for (StageTask task: tasks) {
monitor(task);
}
}
public ReadOnlyObjectProperty<StageTask> currentTaskProperty() {
return currentTask.getReadOnlyProperty();
}
public ReadOnlyStringProperty currentTaskNameProperty() {
return currentTaskName.getReadOnlyProperty();
}
public ReadOnlyDoubleProperty currentTaskProgressProperty() {
return currentTaskProgress.getReadOnlyProperty();
}
public ReadOnlyBooleanProperty idleProperty() {
return idle.getReadOnlyProperty();
}
}
class StageTask extends Task<Duration> {
final private ReadOnlyStringWrapper name;
final private int minMinutesElapsed;
final private int maxMinutesElapsed;
public StageTask(String name, int minMinutesElapsed, int maxMinutesElapsed) {
this.name = new ReadOnlyStringWrapper(name);
this.minMinutesElapsed = minMinutesElapsed;
this.maxMinutesElapsed = maxMinutesElapsed;
}
@Override protected Duration call() throws Exception {
Duration duration = timeInRange(
minMinutesElapsed, maxMinutesElapsed
);
for (int i = 0; i < 25; i++) {
updateProgress(i, 25);
Thread.sleep((int) (duration.toMinutes()));
}
updateProgress(25, 25);
return duration;
}
private Duration timeInRange(int min, int max) {
return Duration.minutes(
random.nextDouble() * (max - min) + min
);
}
public ReadOnlyStringProperty nameProperty() {
return name.getReadOnlyProperty();
}
}
public static void main(String[] args) {
Application.launch(Triathlon.class);
}
}
更新附加问题
假设每个阶段都是一个独立的项目(就像奥运会一样),而不是铁人三项赛。所以游泳、骑自行车、跑步等都是 SportService 的实例。他们同时执行。在体育场的电子记分牌上有一个进度指示器表盘,由所有体育服务游泳、自行车、跑步等共享。它为我提供了大致的总体进度 - 尽管我意识到这很模糊,但它是对一切进展情况的总结,而没有看到细节每个事件的。
使用中定义的机制并行运行事件创建多个并行任务 https://forums.oracle.com/forums/thread.jspa?messageID=10810601。为您的整体奥运进度创建一个进度指示器,并使用低级绑定 API 将其绑定到所有任务的进度总和。
ObservableList<Service> services = FXCollections.observableArrayList();
. . . add services to list.
// extract the progress property for each of the added services.
final ReadOnlyDoubleProperty[] taskProgressList = new ReadOnlyDoubleProperty[services.size()];
for (int i = 0; i < taskProgressList.length; i++) {
taskProgressList[i] = services.get(i).progressProperty();
}
// calculate the average progress of all services.
DoubleBinding overallProgress = Bindings.createDoubleBinding(new Callable<Double>() {
@Override public Double call() throws Exception {
double value = 0;
for (int i = 0; i < taskProgressList.length; i++) {
value += taskProgressList[i].get();
}
value /= taskProgressList.length;
return value;
}
}, taskProgressList);
// bind the overall progress to our indicator
ProgressIndicator overallProgressIndicator = new ProgressIndicator();
overallProgressIndicator.progressProperty().bind(overallProgress);
这是另一个示例,演示了总体进度的使用DoubleBinding
.
import java.io.*;
import java.net.URL;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.*;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class FirstLineSequentialVsParallelService extends Application {
private static final String[] URLs = {
"http://www.google.com",
"http://www.yahoo.com",
"http://www.microsoft.com",
"http://www.oracle.com"
};
private ExecutorService sequentialFirstLineExecutor;
private ExecutorService parallelFirstLineExecutor;
@Override public void init() throws Exception {
sequentialFirstLineExecutor = Executors.newFixedThreadPool(
1,
new FirstLineThreadFactory("sequential")
);
parallelFirstLineExecutor = Executors.newFixedThreadPool(
URLs.length,
new FirstLineThreadFactory("parallel")
);
}
@Override
public void stop() throws Exception {
parallelFirstLineExecutor.shutdown();
parallelFirstLineExecutor.awaitTermination(3, TimeUnit.SECONDS);
sequentialFirstLineExecutor.shutdown();
sequentialFirstLineExecutor.awaitTermination(3, TimeUnit.SECONDS);
}
public static void main(String[] args) { launch(args); }
@Override public void start(Stage stage) throws Exception {
final VBox messages = new VBox();
messages.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
messages.getChildren().addAll(
new Label("Parallel Execution"),
new Label("------------------")
);
DoubleBinding parallelProgress = fetchFirstLines(messages, parallelFirstLineExecutor);
ProgressMonitoredLabel parallelProgressSummary = new ProgressMonitoredLabel("Parallel Execution Summary");
parallelProgressSummary.progress.progressProperty().bind(parallelProgress);
messages.getChildren().add(parallelProgressSummary);
messages.getChildren().addAll(
new Label("Sequential Execution"),
new Label("--------------------")
);
DoubleBinding sequentialProgress = fetchFirstLines(messages, sequentialFirstLineExecutor);
ProgressMonitoredLabel sequentialProgressSummary = new ProgressMonitoredLabel("Sequential Execution Summary");
sequentialProgressSummary.progress.progressProperty().bind(sequentialProgress);
messages.getChildren().add(sequentialProgressSummary);
messages.setStyle("-fx-font-family: monospace;");
stage.setScene(new Scene(messages, 600, 650));
stage.show();
}
private DoubleBinding fetchFirstLines(final VBox monitoredLabels, ExecutorService executorService) {
ObservableList<Service> services = FXCollections.observableArrayList();
for (final String url: URLs) {
final FirstLineService service = new FirstLineService();
service.setExecutor(executorService);
service.setUrl(url);
final ProgressMonitoredLabel monitoredLabel = new ProgressMonitoredLabel(url);
monitoredLabels.getChildren().add(monitoredLabel);
monitoredLabel.progress.progressProperty().bind(service.progressProperty());
service.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override public void handle(WorkerStateEvent t) {
monitoredLabel.addStrings(
service.getMessage(),
service.getValue()
);
}
});
service.start();
services.add(service);
}
final ReadOnlyDoubleProperty[] taskProgressList = new ReadOnlyDoubleProperty[services.size()];
for (int i = 0; i < taskProgressList.length; i++) {
taskProgressList[i] = services.get(i).progressProperty();
}
return Bindings.createDoubleBinding(new Callable<Double>() {
@Override public Double call() throws Exception {
double value = 0;
for (int i = 0; i < taskProgressList.length; i++) {
value += taskProgressList[i].get();
}
value /= taskProgressList.length;
return value;
}
}, taskProgressList);
}
public class ProgressMonitoredLabel extends HBox {
final ProgressBar progress;
final VBox labels;
public ProgressMonitoredLabel(String initialString) {
super(20);
progress = new ProgressBar();
labels = new VBox();
labels.getChildren().addAll(new Label(initialString), new Label());
progress.setPrefWidth(100);
progress.setMinWidth(ProgressBar.USE_PREF_SIZE);
HBox.setHgrow(labels, Priority.ALWAYS);
setMinHeight(60);
getChildren().addAll(progress, labels);
}
public void addStrings(String... strings) {
for (String string: strings) {
labels.getChildren().add(
labels.getChildren().size() - 1,
new Label(string)
);
}
}
}
public static class FirstLineService extends Service<String> {
private StringProperty url = new SimpleStringProperty(this, "url");
public final void setUrl(String value) { url.set(value); }
public final String getUrl() { return url.get(); }
public final StringProperty urlProperty() { return url; }
protected Task createTask() {
final String _url = getUrl();
return new Task<String>() {
{ updateProgress(0, 100); }
protected String call() throws Exception {
updateMessage("Called on thread: " + Thread.currentThread().getName());
URL u = new URL(_url);
BufferedReader in = new BufferedReader(
new InputStreamReader(u.openStream()));
String result = in.readLine();
in.close();
// pause just so that it really takes some time to run the task
// so that parallel execution behaviour can be observed.
for (int i = 0; i < 100; i++) {
updateProgress(i, 100);
Thread.sleep(50);
}
return result;
}
};
}
}
static class FirstLineThreadFactory implements ThreadFactory {
static final AtomicInteger poolNumber = new AtomicInteger(1);
private final String type;
public FirstLineThreadFactory(String type) {
this.type = type;
}
@Override public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable, "LineService-" + poolNumber.getAndIncrement() + "-thread-" + type);
thread.setDaemon(true);
return thread;
}
}
}