Я хочу добавить в свое приложение часы, которые сообщают, как долго вы выполняете задачу. Чтобы упростить его, я включил счетчик, который увеличивается каждую секунду в новом потоке, и обновляю метку setTimer номером счетчика. Для этого у меня есть метка fx:id="setTimer" в моем файле .fxml, и я импортировал ее в свой класс.
@FXML
private Label setTimer;
И создал еще один класс в моем классе, который расширяет поток TimerTask и увеличивает счетчик на единицу при каждом вызове. Создан новый объект «текст», который всегда должен обновляться с текущим значением счетчика.
SimpleStringProperty text = new SimpleStringProperty("undefined");
public class MyTask extends TimerTask {
@Override
public void run() {
counter++;
text.set(Integer.toString(counter));
}
}
Чтобы этот класс вызывался каждую секунду, я создал таймер в методе инициализации и установил его на одну секунду.
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
MyTask myTask = new MyTask();
Timer timer = new Timer(true);
timer.scheduleAtFixedRate(myTask, 0 , 1000);
setTimer.textProperty().bind(text);
}
На данный момент я получаю исключение «Не в потоке приложения FX; currentThread = Таймер-0'.
Я пробовал много способов решить свою проблему, но я не дошел до нужного момента. Мое представление о том, что я хочу сделать, должно быть ясным, и я был бы рад, если бы кто-то мог мне помочь. Моя проблема заключается в обновлении изменений счетчика в графическом интерфейсе. Это не нужно решать так, как я думал, просто нужен совет, как лучше всего это реализовать.
Спасибо
Кроме того, чтобы избежать исключения при выполнении чего-либо из другого потока, можно избежать, если вы поместите изменяющийся код внутрь Platform.runLater(new Runnable(){ /* ... */ });
a) использовать поддержку параллелизма/анимации fx b) что бы вы ни использовали, вы не должны обновлять узлы (или какие-либо их свойства) вне потока приложения fx c) при запросе помощи в отладке предоставьте минимально воспроизводимый пример демонстрирующий, что не работает, как ожидалось
Хорошо, мои комментарии слишком длинные. Вот как бы я попробовал это сделать.
sw.getTime(TimeUntis.seconds)
). Преобразуйте это в часы и минуты, если хотите , как показано в этом посте SO Platform.runLater(new Runnable(){ /* access ui element and write time here */ });
Олег, я так долго решал эту проблему. Вы очень помогли мне с «Platform.runLater (new Runnable())», и теперь он работает. Я добавил это в свой run() в классе MyTask Platform.runLater (new Runnable () { @Override public void run () { text.set (Integer.toString (counter)); }});
Большое спасибо.
Не за что, всегда рад помочь!
Использование Platform.runLater() в фоновом потоке — это своего рода грязный хлам, которого, вероятно, следует избегать. JavaFX имеет механизмы для обработки такого рода вещей, которые вы должны использовать. В частности, Task<> позволяет фоновым потокам обновлять данные, связанные с элементами экрана JavaFX, которые необходимо обновить на FXAT.
Вы МОЖЕТЕ делать то, что пытаетесь сделать с помощью задачи JavaFX, но использование таймера Java внутри нее кажется невозможным, поскольку поток Java не может ожидать завершения таймера. Итак, вместо этого я использовал цикл for со сном, чтобы сделать то же самое. Это неуклюже, но демонстрирует, как связать частичные результаты задачи с отображением на экране:
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Timer1(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
public class Timer1 extends VBox {
public Timer1() {
Text time = new Text();
Button startButton = new Button("Start");
Button stopButton = new Button("Stop");
getChildren().addAll(time, startButton, stopButton);
startButton.setOnAction(startEvt -> {
Task<Integer> timerFxTask = new Task<>() {
{
updateValue(0);
}
@Override
protected Integer call() throws Exception {
for (int counter = 0; counter <= 1000; counter++) {
sleep(1000);
updateValue(counter);
}
return 1000;
}
};
stopButton.setOnAction(stopEvt -> timerFxTask.cancel());
time.textProperty().bind(Bindings.createStringBinding(() -> timerFxTask.getValue().toString(),
timerFxTask.valueProperty()));
Thread timerThread = new Thread(timerFxTask);
timerThread.start();
});
}
}
Но есть лучший способ сделать то, что вы пытаетесь сделать, который по сути является анимацией, и у JavaFX есть возможность сделать именно это. Обычно люди используют анимацию для преобразования внешнего вида элементов экрана JavaFX, но вы также можете использовать ее для анимации содержимого текста с течением времени. Что я сделал здесь, так это создал IntegerProperty, которое может быть преобразовано из начального значения в конечное значение, линейно интерполированное во времени, а затем привязал это значение к TextProperty текста на экране. Таким образом, вы видите, что он обновляется один раз в секунду.
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Timer2(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
public class Timer2 extends VBox {
public Timer2() {
Text time = new Text();
Button startButton = new Button("Start");
Button stopButton = new Button("Stop");
getChildren().addAll(time, startButton, stopButton);
startButton.setOnAction(startEvt -> {
IntegerProperty counter = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1000), new KeyValue(counter, 1000)));
stopButton.setOnAction(stopEvt -> timeline.stop());
time.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(counter.get()), counter));
timeline.play();
});
}
}
Спасибо за уделенное время, завтра обязательно попробую
Добро пожаловать в СО. Возможно, вы сможете использовать секундомер, например
org.apache.commons.lang.time.StopWatch
(документация: commons.apache.org/proper/commons-lang/apidocs/org/apache/…). Запустите его при запуске вашего приложения и используйте getTime, чтобы получить время. Я не уверен, есть ли порперти для привязки или нет.