gpt4 book ai didi

Java 计时器 - 使用 Platform.runLater 更新标签

转载 作者:行者123 更新时间:2023-12-02 08:41:53 25 4
gpt4 key购买 nike

此代码示例是 Stopwatch 类的一部分,该类是一个更大项目的一部分,该项目旨在成为一个模仿 Android 时钟的桌面 GUI 应用程序。我有秒、分钟、小时等标签,这些标签应该从定时器任务内的无限 while 循环进行更新,该定时器任务在 boolean 状态为 true 时运行。 while 循环应该实时更新 GUI 标签。我让计时器任务每毫秒执行一次。为什么程序一更新第一个标签,我的 GUI 就会挂起?我该如何解决这个问题?下面是代码。

static int Milliseconds = 0;

static int Seconds = 0;

static int Minutes = 0;

static int Hours = 0;

static int Days = 0;

static Boolean State = false;

public static void display(){
Stage window = new Stage();
window.initModality(Modality.APPLICATION_MODAL);
window.setTitle("Timer");
window.setMinWidth(250);
window.setMinHeight(500);
GridPane gp = new GridPane();

Label days = new Label("0");
gp.setConstraints(days, 0,0);

Label hours = new Label("0");
gp.setConstraints(hours, 1,0);

Label minutes = new Label("0");
gp.setConstraints(minutes,2,0);

Label seconds = new Label("0");
gp.setConstraints(seconds,3,0);

Label milliseconds = new Label("0");
gp.setConstraints(milliseconds, 4,0);

//Handler mainHandler = new Handler()
// Task<Void> longRunningTask = new Task<Void>(){}
Timer mt = new Timer();

//Platform.runLater is not updating gui. It hangs the gui instead
TimerTask tm = new TimerTask() {
@Override
public void run() {

Platform.runLater(() -> {


for (; ; ) {
long timebefore = System.currentTimeMillis();
if (State) {
try {

if (Milliseconds > 999) {
Milliseconds = 0;
Seconds++;
}
if (Seconds > 59) {
Milliseconds = 0;
Seconds = 0;
Minutes++;
}
if (Minutes > 59) {
Milliseconds = 0;
Seconds = 0;
Minutes = 0;
Hours++;
}
if (Hours > 23) {
Milliseconds = 0;
Seconds = 0;
Minutes = 0;
Hours = 0;
Days++;
}
milliseconds.setText(" : " + Milliseconds);
Milliseconds++;
seconds.setText(" : " + Seconds);
minutes.setText(" : " + Minutes);
hours.setText(" : " + Hours);
days.setText(" : " + Days);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});





}
};

Button start = new Button("Start");
gp.setConstraints(start, 0,1);
start.setOnAction(event -> {
State = true;
mt.scheduleAtFixedRate(tm, 1,1);
});
Button stop = new Button("Stop");
gp.setConstraints(stop,1,1);
stop.setOnAction(event-> {
State = false;
});

Button restart = new Button("Restart");
gp.setConstraints(restart, 2,1);
restart.setOnAction(event-> {
State = false;
Milliseconds = 0;
Seconds = 0;
Minutes = 0;
Hours = 0;
Days = 0;
});





gp.getChildren().addAll(milliseconds,seconds, minutes, hours, days, start, stop, restart);
Scene scene = new Scene(gp);
window.setScene(scene);
window.showAndWait();
}
public void Start(Timer mt){

}

最佳答案

您传递给 Platform#runLater(Runnable)Runnable 包含一个无限循环。这意味着您在 JavaFX 应用程序线程上执行无限循环,这就是您的 UI 变得无响应的原因。如果 FX 线程无法自由地完成其工作,则无法处理用户生成的事件,并且无法调度渲染“脉冲”。后一点就是为什么尽管您连续调用 setText(...),UI 也不会更新。

如果您想继续当前的方法,修复方法是从 Runnable 实现中删除 for (;;) 循环。您将 TimerTask 设置为每毫秒执行一次,这意味着您所要做的就是计算新状态并在每次执行时设置标签一次。换句话说,run() 方法已经“循环”了。例如:

TimerTask task = new TimerTask() {
@Override public void run() {
Platform.runLater(() -> {
// calculate new state...

// update labels...

// return (no loop!)
});
}
};

也就是说,没有理由为此使用后台线程。我建议使用animation API由 JavaFX 提供。它是异步的,但在 FX 线程上执行,使其更易于实现和推理 — 使用多个线程总是更复杂。要执行与您当前正在执行的操作类似的操作,您可以使用 TimelinePauseTransition 代替 java.util.TimerJavaFX periodic background task问答提供了一些为此目的使用动画的好例子。

就我个人而言,我会使用 AnimationTimer实现秒表。这是一个例子:

import java.util.concurrent.TimeUnit;
import javafx.animation.AnimationTimer;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyLongProperty;
import javafx.beans.property.ReadOnlyLongWrapper;

public class Stopwatch {

private static long toMillis(long nanos) {
return TimeUnit.NANOSECONDS.toMillis(nanos);
}

// value is in milliseconds
private final ReadOnlyLongWrapper elapsedTime = new ReadOnlyLongWrapper(this, "elapsedTime");
private void setElapsedTime(long elapsedTime) { this.elapsedTime.set(elapsedTime); }
public final long getElapsedTime() { return elapsedTime.get(); }
public final ReadOnlyLongProperty elapsedTimeProperty() { return elapsedTime.getReadOnlyProperty(); }

private final ReadOnlyBooleanWrapper running = new ReadOnlyBooleanWrapper(this, "running");
private void setRunning(boolean running) { this.running.set(running); }
public final boolean isRunning() { return running.get(); }
public final ReadOnlyBooleanProperty runningProperty() { return running.getReadOnlyProperty(); }

private final Timer timer = new Timer();

public void start() {
if (!isRunning()) {
timer.start();
setRunning(true);
}
}

public void stop() {
if (isRunning()) {
timer.pause();
setRunning(false);
}
}

public void reset() {
timer.stopAndReset();
setElapsedTime(0);
setRunning(false);
}

private class Timer extends AnimationTimer {

private long originTime = Long.MIN_VALUE;
private long pauseTime = Long.MIN_VALUE;
private boolean pausing;

@Override
public void handle(long now) {
if (pausing) {
pauseTime = toMillis(now);
pausing = false;
stop();
} else {
if (originTime == Long.MIN_VALUE) {
originTime = toMillis(now);
} else if (pauseTime != Long.MIN_VALUE) {
originTime += toMillis(now) - pauseTime;
pauseTime = Long.MIN_VALUE;
}

setElapsedTime(toMillis(now) - originTime);
}
}

@Override
public void start() {
pausing = false;
super.start();
}

void pause() {
if (originTime != Long.MIN_VALUE) {
pausing = true;
} else {
stop();
}
}

void stopAndReset() {
stop();
originTime = Long.MIN_VALUE;
pauseTime = Long.MIN_VALUE;
pausing = false;
}
}
}

警告:当AnimationTimer运行时,Stopwatch实例无法被垃圾回收。

上面公开了一个属性,elapsedTime,它表示耗时(以毫秒为单位)。根据该值,您可以计算自启动秒表以来已经过去的天数、小时数、分钟数、秒数和毫秒数。您只需监听该属性并在属性更改时更新 UI。

关于Java 计时器 - 使用 Platform.runLater 更新标签,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61330924/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com