Ern*_*P W 1 java animation javafx
我想制作一个带有动画文本的通知窗口。单击按钮即可发送通知,并且动画将开始播放。我的问题是,当我在上一个动画完成之前再次单击按钮时,会同时执行两个动画。如何使“sendMessage()”的每个方法调用等待另一个方法完成?如果它有任何意义的话,我的程序中有多个节点调用 sendMessage() 方法,这与我的 MRE 不同,所以我想要某种带有消息的队列。这是我的 MRE:
public class AnimationTest extends Application {
private final Label messageLabel = new Label();
@Override
public void start(Stage stage) throws IOException {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
Scene scene = new Scene(vBox, 320, 240);
vBox.getChildren().add(messageLabel);
Button button = new Button();
button.setOnAction(event -> sendMessage("Some animated text."));
vBox.getChildren().add(button);
stage.setScene(scene);
stage.show();
}
private void sendMessage(String message) {
final IntegerProperty i = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(
Duration.millis(40),
event -> {
if (i.get() > message.length()) {
timeline.stop();
} else {
messageLabel.setText(message.substring(0, i.get()));
i.set(i.get() + 1);
}
});
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
public static void main(String[] args) {
launch();
}
}
Run Code Online (Sandbox Code Playgroud)
对于您发布的具体示例,最简单的方法是在开始动画之前立即禁用按钮,并在动画停止时再次启用它。这是执行此操作的一种方法:
public class AnimationTest extends Application {
private final Label messageLabel = new Label();
@Override
public void start(Stage stage) {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
Scene scene = new Scene(vBox, 320, 240);
vBox.getChildren().add(messageLabel);
Button button = new Button();
button.setOnAction(event -> {
Animation animation = sendMessage("Some animated text.");
button.disableProperty().bind(Bindings.equal(animation.statusProperty(), Animation.Status.RUNNING));
});
vBox.getChildren().add(button);
stage.setScene(scene);
stage.show();
}
private Animation sendMessage(String message) {
final IntegerProperty i = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(
Duration.millis(40),
event -> {
if (i.get() > message.length()) {
timeline.stop();
} else {
messageLabel.setText(message.substring(0, i.get()));
i.set(i.get() + 1);
}
});
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
return timeline ;
}
public static void main(String[] args) {
launch();
}
}
Run Code Online (Sandbox Code Playgroud)
如果您希望允许这些消息累积在队列中,并在旧动画完成时启动新动画,则需要保留消息队列以及对当前正在运行的动画的引用(如果有)。如果当前没有正在运行的动画,您可以从队列中轮询AnimationTimer并在出现新消息时启动新动画。
我建议您考虑一下这是否是您想要采取的方法;这里不能保证您的消息的显示速度不会快于动画的速度,在这种情况下,队列将无限期地增长。但是,如果您可以以其他方式确保情况并非如此,那么这就是一个实现:
public class AnimationTest extends Application {
private final Label messageLabel = new Label();
private final Queue<String> messages = new LinkedList<>();
private Animation currentAnimation = null ;
@Override
public void start(Stage stage) {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
Scene scene = new Scene(vBox, 320, 240);
vBox.getChildren().add(messageLabel);
Button button = new Button();
button.setOnAction(event -> messages.add("Some animated text."));
AnimationTimer timer = new AnimationTimer() {
@Override
public void handle(long l) {
if (currentAnimation == null || currentAnimation.getStatus() == Animation.Status.STOPPED) {
String message = messages.poll();
if (message != null) {
currentAnimation = sendMessage(message);
currentAnimation.play();
}
}
}
};
timer.start();
vBox.getChildren().add(button);
stage.setScene(scene);
stage.show();
}
private Animation sendMessage(String message) {
final IntegerProperty i = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(
Duration.millis(40),
event -> {
if (i.get() > message.length()) {
timeline.stop();
} else {
messageLabel.setText(message.substring(0, i.get()));
i.set(i.get() + 1);
}
});
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE);
return timeline ;
}
public static void main(String[] args) {
launch();
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,这里没有考虑线程。该handle()方法在 FX 应用程序线程上调用,因此唯一的要求是将消息放置在同一线程的队列中。在此示例中会发生这种情况,因为按钮的事件处理程序是在该线程上调用的。如果您的消息来自后台线程,您应该确保将它们添加到 FX 应用程序线程上的队列中,方法是使用或 (最好)使用 JavaFX 并发 API(即通过检索orPlatform.runLater(...)中的消息并将它们添加到到处理程序中的队列)。TaskServiceonSucceeded