繁体   English   中英

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

[英]Java Timer - Updating Labels with Platform.runLater

此代码示例是 Stopwatch class 的一部分,它是一个更大的项目的一部分,该项目旨在成为仿照 Android 时钟的桌面 gui 应用程序。 我有秒、分钟、小时等的标签,这些标签应该从无限循环中更新,该循环位于计时器任务内,当 boolean state 为真时运行。 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){

    }

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

如果您想继续当前的方法,解决方法是从Runnable实现中删除for (;;)循环。 您将TimerTask设置为每毫秒执行一次,这意味着您所要做的就是计算新的 state 并在每次执行时设置一次标签。 换句话说, 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.Timer JavaFX 定期后台任务问答提供了一些为此目的使用动画的好例子。

就个人而言,我会使用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。

暂无
暂无

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

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