在 运行 FXML 中的下一个函数之前停止 X 秒(使用 SCENEBUILDER)

stop for X seconds before running next function in JAVA FXML (using SCENEBUILDER)

我使用 Scenebuilder 在我的 GUI 中放置了几个形状(我的项目的简化版本)。我希望形状改变颜色,但在改变颜色之间等待 2 秒。我希望在按下按钮后在我的控制器 class 中发生这些更改。

Circle1.setFill(YELLOW)
Wait(2 seconds)
Circle2.setFill(BLUE)

我不知道该怎么做。我在网上阅读过有关线程的内容,但我并不真正了解如何从我的 Main 到我的 Controller class 实现它。另外,我真的无法在网上找到任何示例。我的主要 class 看起来像:

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("File.fxml"));
        Scene scene = new Scene(root);
        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.show();    
    }
}

请帮忙。另外,如果你能提供一个例子,那将有助于我理解,因为我在网上找不到一个例子。

我认为通过一个例子回答这个问题是最简单的。因此,我创建了一个小型交通灯应用程序,因为它允许我使用 Circle 和类似于您的问题的时间序列,同时对所有人来说都是一个熟悉的概念。

我将使用 java.util.Timerjava.util.TimerTask 来处理灯光序列。您可以选择在 JavaFX 中使用一些动画/时间线,但我认为这对于此类任务来说太过分了。

我包含了这个项目中使用的三个文件:

  • FXMLTrafficLight.fxml - 定义我的 FXML 布局
  • FXMLTrafficLightController.java - 我的 FXML 控制器
  • TrafficLightApplication.java - 为了完整起见,我的 Application 子类,这只是样板。

FXMLTrafficLight.fxml

不是花哨的布局,只是一个 VBox 三个圆圈 redLightamberLightgreenLight,加上两个 Button 个对象 startLightsstopLights 用于启动和停止计时器。

<VBox fx:id="root" id="VBox" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxtimer.FXMLTrafficLightController">
    <children>
        <Circle fx:id="redLight" radius="100"></Circle>
        <Circle fx:id="amberLight" radius="100"></Circle>
        <Circle fx:id="greenLight" radius="100"></Circle>
        <Button fx:id="startLights" text="Start Lights" onAction="#startLights"></Button>
        <Button fx:id="stopLights" text="Start Lights" onAction="#stopLights"></Button>
    </children>
</VBox>

FXMLTrafficLightController.java

为了简单起见,我在控制器中包含了 model/state。灯是红色/琥珀色/绿色由 boolean 标志决定。初始状态在 initialize() 方法中设置,并通过调用 updateState().

进行更新

当调用 startLights(ActionEvent) 时(startLightsEventHandler),将使用首先调用 updateState() 在由 Timer 创建的线程上,然后调用 updateLights(),它使用 Platform.runLater(Runnable).

根据 JavaFX 应用程序线程上的当前状态更改灯光的颜色

注意:TimerTask 本身不会在 JavaFX 应用程序线程上 运行,因此需要使用 Platform.runLater(Runnable) 来更新 GUI。

调用stopLights(ActionEvent)时,会取消Timer

请注意,startLights(ActionEvent)stopLights(ActionEvent) 也会切换在界面上启用哪些 Button 对象。

public class FXMLTrafficLightController implements Initializable {

    @FXML
    private Circle redLight;

    @FXML
    private Circle amberLight;

    @FXML
    private Circle greenLight;

    @FXML
    private Button startLights;

    @FXML
    private Button stopLights;

    private Timer timer;
    private static final int DELAY = 2000; // ms

    private boolean red, amber, green;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        red = true;
        amber = false;
        green = false;
        stopLights.setDisable(true);
        updateLights();
    }

    @FXML
    private void startLights(ActionEvent e) {
        toggleButtons();
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                // Not run on the JavaFX Application Thread!
                updateState();
                // Using Platform.runLater(Runnable) to ensure updateLights()
                // is run on the JavaFX Application Thread
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        updateLights();
                    }
                });
            }
        }, 0, DELAY); // no initial delay, trigger again every 2000 ms (DELAY)
    }

    @FXML
    private void stopLights(ActionEvent e) {
        toggleButtons();
        timer.cancel();        
    }

    private void toggleButtons() {        
        startLights.setDisable(!startLights.isDisable());
        stopLights.setDisable(!stopLights.isDisable());
    }

    private void updateState() {
        if (red && !amber && !green) {
            amber = true;
        } else if (red && amber && !green) {
            red = false;
            amber = false;
            green = true;
        } else if (!red && !amber && green) {
            green = false;
            amber = true;
        } else {
            red = true;
            amber = false;
            green = false;
        }
    }

    private void updateLights() {
        redLight.setFill(red ? Color.RED : Color.GREY);
        amberLight.setFill(amber ? Color.ORANGE : Color.GREY);
        greenLight.setFill(green ? Color.GREEN : Color.GREY);
    }
}

TrafficLightApplication.java

为了完整性...只是更改了文件名的标准样板。

public class TrafficLightApplication extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLTrafficLight.fxml"));        
        Scene scene = new Scene(root);        
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}