使用线程的javaFX中的计时器
timer in javaFX using thread
我正在尝试编写海战代码,在我的一个菜单上,我应该向客户展示他们的地图,并给他们 30 秒的时间来决定他们是想要一张新地图,还是地图没问题,然后他们点击开始游戏。如果他们点击开始游戏按钮,计时器应该停止并且场景会改变。如果他们的时间到了,就像他们点击了开始游戏按钮一样,超时后场景应该会自动改变。如果他们点击新地图按钮,我应该给他们剩余时间 + 10 来重新决定。我做了一些编码,但我无法完成剩余的 +10 部分,而且我不知道如何停止线程。这是我的场景的 FXML 控制器,计时器应该在其中。drawMap 函数在这里并不重要。
public class StandbyMapGuiController implements Initializable {
@FXML
private volatile Label timerLabel;
@FXML
private GridPane sea;
private static Stage stage;
private static int time;
private GameTimer gameTimer;
private CountDown countDown;
@Override
public void initialize(URL location, ResourceBundle resources) {
drawMap();
gameTimer = new GameTimer(time,timerLabel , this);
gameTimer.countDown();
}
public void updateTimer(int newTime){
timerLabel.setText(String.valueOf(newTime));
}
public void drawMap(){
for (int i = 0 ; i < 10 ; i++){
for (int j = 0 ; j < 10 ; j++){
MapButton btn = new MapButton(i,j);
btn.setManner(MapButton.COLOR.VIOLET);
sea.add(btn,j,i);
}
}
}
public void changeMap(ActionEvent actionEvent) {
int remaining = Integer.parseInt(timerLabel.getText())+10;
System.out.println(remaining);
setTime(remaining);
//restart the page
Toolbar.getInstance().changeScene(ConfigLoader.readProperty("standbyMapMenuAdd"), actionEvent);
}
public void startGame() {
//todo: tell server the gamer is ready
timerLabel.setText("Time's up");
try {
Parent root;
root = FXMLLoader.load(getClass().getClassLoader().getResource("FXMLs/GameBoard.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
这是我的 GameTimer class:
public class GameTimer {
private Timer timer;
private TimerTask task;
private int time;
private volatile Label label;
public GameTimer(int time, Label label, StandbyMapGuiController controller) {
this.time = time;
this.label = label;
timer = new Timer();
task = new TimerTask() {
int counter = time;
boolean timeOut = false;
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
public boolean isTimeOut() {
return timeOut;
}
public void setTimeOut(boolean timeOut) {
this.timeOut = timeOut;
}
@Override
public void run() {
Platform.runLater(() -> {
if (counter > 0) {
label.setText(String.valueOf(counter));
counter--;
} else {
timeOut = true;
controller.startGame();
timer.cancel();
}
});
}
};
}
public void countDown() {
timer.scheduleAtFixedRate(task, 0, 1000);
}
}
我无法访问超时和计数器来设置或获取它们的值。(TimerTask 线程中的 getter 和 setter 不起作用)
不要为此使用线程。更好的选择是使用动画。动画是异步的,但在 JavaFX 应用程序线程 上执行。例如,您可以使用 PauseTransition
:
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
@Override
public void start(Stage primaryStage) {
PauseTransition timer = new PauseTransition(Duration.seconds(30));
timer.setOnFinished(
e -> {
Alert alert = new Alert(AlertType.INFORMATION);
alert.initOwner(primaryStage);
alert.setHeaderText(null);
alert.setContentText("You took too long! Will now exit application.");
alert.setOnHidden(we -> Platform.exit());
alert.show();
});
Button button = new Button("Add ten seconds");
button.setOnAction(
e -> {
e.consume();
timer.jumpTo(timer.getCurrentTime().subtract(Duration.seconds(10)));
});
Label timerLabel = new Label();
timerLabel
.textProperty()
.bind(
Bindings.createStringBinding(
() -> {
Duration currentTime = timer.getCurrentTime();
Duration duration = timer.getDuration();
double timeRemaining = duration.subtract(currentTime).toSeconds();
return String.format("%04.1f seconds remaining", timeRemaining);
},
timer.currentTimeProperty(),
timer.durationProperty()));
ProgressBar timerProgress = new ProgressBar();
timerProgress.setMaxWidth(Double.MAX_VALUE);
timerProgress
.progressProperty()
.bind(
Bindings.createDoubleBinding(
() -> {
double currentTime = timer.getCurrentTime().toMillis();
double duration = timer.getDuration().toMillis();
return 1.0 - (currentTime / duration);
},
timer.currentTimeProperty(),
timer.durationProperty()));
StackPane root = new StackPane(button, timerLabel, timerProgress);
root.setPadding(new Insets(10));
StackPane.setAlignment(timerLabel, Pos.TOP_LEFT);
StackPane.setAlignment(timerProgress, Pos.BOTTOM_CENTER);
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
timer.playFromStart();
}
}
PauseTransition
有效地从零开始计数到持续时间(在本例中为 30 秒)。这就是标签和进度条“反转”时间值以给出剩余时间而不是经过时间的原因。
请注意 jumpTo
方法永远不会低于零或高于持续时间。换句话说,如果用户在剩余 25 秒时按下按钮,则时间将增加到剩余 30 秒。如果您希望在该场景中将时间增加到剩余 35 秒,则更改:
timer.jumpTo(timer.getCurrentTime().subtract(Duration.seconds(10)));
收件人:
timer.pause();
Duration currentTime = timer.getCurrentTime();
Duration duration = timer.getDuration();
timer.setDuration(duration.subtract(currentTime).add(Duration.seconds(10)));
timer.playFromStart();
我正在尝试编写海战代码,在我的一个菜单上,我应该向客户展示他们的地图,并给他们 30 秒的时间来决定他们是想要一张新地图,还是地图没问题,然后他们点击开始游戏。如果他们点击开始游戏按钮,计时器应该停止并且场景会改变。如果他们的时间到了,就像他们点击了开始游戏按钮一样,超时后场景应该会自动改变。如果他们点击新地图按钮,我应该给他们剩余时间 + 10 来重新决定。我做了一些编码,但我无法完成剩余的 +10 部分,而且我不知道如何停止线程。这是我的场景的 FXML 控制器,计时器应该在其中。drawMap 函数在这里并不重要。
public class StandbyMapGuiController implements Initializable {
@FXML
private volatile Label timerLabel;
@FXML
private GridPane sea;
private static Stage stage;
private static int time;
private GameTimer gameTimer;
private CountDown countDown;
@Override
public void initialize(URL location, ResourceBundle resources) {
drawMap();
gameTimer = new GameTimer(time,timerLabel , this);
gameTimer.countDown();
}
public void updateTimer(int newTime){
timerLabel.setText(String.valueOf(newTime));
}
public void drawMap(){
for (int i = 0 ; i < 10 ; i++){
for (int j = 0 ; j < 10 ; j++){
MapButton btn = new MapButton(i,j);
btn.setManner(MapButton.COLOR.VIOLET);
sea.add(btn,j,i);
}
}
}
public void changeMap(ActionEvent actionEvent) {
int remaining = Integer.parseInt(timerLabel.getText())+10;
System.out.println(remaining);
setTime(remaining);
//restart the page
Toolbar.getInstance().changeScene(ConfigLoader.readProperty("standbyMapMenuAdd"), actionEvent);
}
public void startGame() {
//todo: tell server the gamer is ready
timerLabel.setText("Time's up");
try {
Parent root;
root = FXMLLoader.load(getClass().getClassLoader().getResource("FXMLs/GameBoard.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
这是我的 GameTimer class:
public class GameTimer {
private Timer timer;
private TimerTask task;
private int time;
private volatile Label label;
public GameTimer(int time, Label label, StandbyMapGuiController controller) {
this.time = time;
this.label = label;
timer = new Timer();
task = new TimerTask() {
int counter = time;
boolean timeOut = false;
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
public boolean isTimeOut() {
return timeOut;
}
public void setTimeOut(boolean timeOut) {
this.timeOut = timeOut;
}
@Override
public void run() {
Platform.runLater(() -> {
if (counter > 0) {
label.setText(String.valueOf(counter));
counter--;
} else {
timeOut = true;
controller.startGame();
timer.cancel();
}
});
}
};
}
public void countDown() {
timer.scheduleAtFixedRate(task, 0, 1000);
}
}
我无法访问超时和计数器来设置或获取它们的值。(TimerTask 线程中的 getter 和 setter 不起作用)
不要为此使用线程。更好的选择是使用动画。动画是异步的,但在 JavaFX 应用程序线程 上执行。例如,您可以使用 PauseTransition
:
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
@Override
public void start(Stage primaryStage) {
PauseTransition timer = new PauseTransition(Duration.seconds(30));
timer.setOnFinished(
e -> {
Alert alert = new Alert(AlertType.INFORMATION);
alert.initOwner(primaryStage);
alert.setHeaderText(null);
alert.setContentText("You took too long! Will now exit application.");
alert.setOnHidden(we -> Platform.exit());
alert.show();
});
Button button = new Button("Add ten seconds");
button.setOnAction(
e -> {
e.consume();
timer.jumpTo(timer.getCurrentTime().subtract(Duration.seconds(10)));
});
Label timerLabel = new Label();
timerLabel
.textProperty()
.bind(
Bindings.createStringBinding(
() -> {
Duration currentTime = timer.getCurrentTime();
Duration duration = timer.getDuration();
double timeRemaining = duration.subtract(currentTime).toSeconds();
return String.format("%04.1f seconds remaining", timeRemaining);
},
timer.currentTimeProperty(),
timer.durationProperty()));
ProgressBar timerProgress = new ProgressBar();
timerProgress.setMaxWidth(Double.MAX_VALUE);
timerProgress
.progressProperty()
.bind(
Bindings.createDoubleBinding(
() -> {
double currentTime = timer.getCurrentTime().toMillis();
double duration = timer.getDuration().toMillis();
return 1.0 - (currentTime / duration);
},
timer.currentTimeProperty(),
timer.durationProperty()));
StackPane root = new StackPane(button, timerLabel, timerProgress);
root.setPadding(new Insets(10));
StackPane.setAlignment(timerLabel, Pos.TOP_LEFT);
StackPane.setAlignment(timerProgress, Pos.BOTTOM_CENTER);
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
timer.playFromStart();
}
}
PauseTransition
有效地从零开始计数到持续时间(在本例中为 30 秒)。这就是标签和进度条“反转”时间值以给出剩余时间而不是经过时间的原因。
请注意 jumpTo
方法永远不会低于零或高于持续时间。换句话说,如果用户在剩余 25 秒时按下按钮,则时间将增加到剩余 30 秒。如果您希望在该场景中将时间增加到剩余 35 秒,则更改:
timer.jumpTo(timer.getCurrentTime().subtract(Duration.seconds(10)));
收件人:
timer.pause();
Duration currentTime = timer.getCurrentTime();
Duration duration = timer.getDuration();
timer.setDuration(duration.subtract(currentTime).add(Duration.seconds(10)));
timer.playFromStart();