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

问题描述 投票:0回答:1

此代码示例是Stopwatch类的一部分,Stopwatch类是一个较大的项目的一部分,该项目旨在成为以Android Clock为后缀的桌面gui应用。我具有秒,分钟,小时等标签,这些标签应该从计时器任务内部的无限while循环中更新,而该计时器任务在布尔状态为true时运行。 while循环应该实时更新GUI标签。我让计时器任务每毫秒执行一次。为什么在程序更新第一个Label后我的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){

    }
java user-interface javafx label stopwatch
1个回答
0
投票

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

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

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

            // update labels...

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

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

我个人将使用AnimationTimer来实现秒表。这是一个例子:

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,它表示经过的时间(以毫秒为单位)。根据该值,您可以计算自启动秒表以来经过的天,小时,分钟,秒和毫秒的数量。您只需要侦听属性并在属性更改时更新UI。

© www.soinside.com 2019 - 2024. All rights reserved.