【问题标题】:JavaFX: Display text in TextArea with delay in between each lineJavaFX:在 TextArea 中显示文本,每行之间有延迟
【发布时间】:2018-04-05 12:25:06
【问题描述】:

我正在尝试在 TextArea 中显示文本,每个句子之间都有延迟,就像您在进行对话一样。 我尝试使用 sleep 功能,但这不起作用,因为只有在所有方法停止运行时才会显示文本。 这样做的有效方法是什么:

(伪代码)

textArea.appendText("Goodday sir, how are you doing?");
(0.5 second delay);
textArea.appendText("I'm fine thanks");
(1 second delay);
textArea.appendText("What can I do for you?");
getPlayerInput();
textArea.appendText("Sure, I'll take care of it.");

澄清我正在尝试做的事情: 在 textArea 中显示文本,中间有延迟,并且能够在中间运行函数。

【问题讨论】:

  • First:前 0.5 秒也应该是一个输入。触发内心的真实主义 - sry.
  • 您可以使用java.util-timer,它代表组件timelineHere 你可以找到它的一个例子。你可以触发后台任务,然后它就会工作。
  • a) 不要阻塞应用程序线程 b) getPlayerInput 可能不起作用,如果您期待任何用户输入,因为 JavaFX 是基于事件的,您可能会阻塞负责的线程用于处理事件。 c) 看看多线程+Platform.runLaterjavafx.animation 包。

标签: java javafx textarea delay


【解决方案1】:

作为另一个答案中时间轴的变体,您可以为要显示的每条消息创建不同的KeyFrame。这样可以避免出现“嵌套时间线”的情况,我认为如果您要一个接一个地显示超过两到三个消息,这种情况会变得难以管理。

这是一个使用这个想法的 SSCCE:

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Conversation extends Application {

    private TextArea console ;
    private TextField input ;
    private BooleanProperty readyForInput ;

    private Timeline createTimeline(String[] messages) {
        Timeline timeline = new Timeline();
        Duration delayBetweenMessages = Duration.seconds(1);
        Duration frame = delayBetweenMessages ;
        for (String msg : messages) {
            timeline.getKeyFrames().add(new KeyFrame(frame, e -> console.appendText(msg+"\n")));
            frame = frame.add(delayBetweenMessages);
        }
        timeline.statusProperty().addListener((obs, oldStatus, newStatus) -> {
            readyForInput.set(newStatus != Animation.Status.RUNNING);
            if (newStatus != Animation.Status.RUNNING) {
                input.requestFocus();
            }
        });
        return timeline ;
    }

    @Override
    public void start(Stage primaryStage) {

        readyForInput = new SimpleBooleanProperty(false);

        console = new TextArea();
        console.setEditable(false);

        input = new TextField();
        input.disableProperty().bind(readyForInput.not());

        input.setOnAction(e -> {
            String inputText = input.getText();
            console.appendText("> "+inputText+"\n");
            input.clear();
            createTimeline(getMessages(inputText)).play();
        });

        BorderPane root = new BorderPane(console, input, null, null, null) ;
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();

        createTimeline(getMessages(null)).play();
    }

    private String[] getMessages(String input) {
        if (input == null || input.isEmpty()) {
            return new String[] {
                    "Goodday sir, how are you doing?",
                    "I'm fine thanks",
                    "What can I do for you?"
            };
        } else {
            // AI logic here...
            return new String[] { "Sure, I'll take care of it." };
        }
    }

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

【讨论】:

  • 你可以使用 setAll 而不是 add 所以如果他想每次使用不同的延迟
  • @JavaNoob 你会在一个数组中循环......如何,确切地???
  • 这正是我所需要的,这使得快速编写对话变得容易。我想我也可以使用使用字符串作为键和值作为持续时间的 HashMap?
  • 刚刚发现一个HashMap没有指定顺序
  • @WouterA 您当然可以使用Map(任何类型)将消息映射到随后的暂停。 (我认为存储在地图中的顺序根本不重要。)或任何其他逻辑;只需将delayBetweenMessages 替换为从消息内容中计算或查找的任何Duration
【解决方案2】:

您可以使用 Timeline 的 onFinished 在 JavaFX 中进行延迟操作

试试下面的代码

package application;

import java.util.ArrayList;
import java.util.Iterator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Main extends Application {
    Timeline delay = new Timeline();
    TextArea textArea = new TextArea();
    boolean waitForInput = false;
    Msg current;

    @Override
    public void start(Stage primaryStage) {

        StackPane root = new StackPane();

        root.getChildren().add(textArea);

        Scene scene = new Scene(root, 500, 500);

        ArrayList<Msg> msgs = new ArrayList<Msg>();
        msgs.add(new Msg("Goodday sir, how are you doing?\n", Duration.seconds(1), false));
        msgs.add(new Msg("i'm fine thanks!\n", Duration.seconds(2), false));
        msgs.add(new Msg("What can I do for you?\n", Duration.seconds(0.1), true));
        msgs.add(new Msg("Sure, I'll take care of it.\n", Duration.seconds(1), false));
        msgs.add(new Msg("....", Duration.seconds(0.5), false));
        msgs.add(new Msg("are you sure it's the only thing you need?\n", Duration.seconds(0.1), true));
        msgs.add(new Msg("alright bye", Duration.seconds(0), true));

        Iterator<Msg> it = msgs.iterator();
        delay.getKeyFrames().setAll(new KeyFrame(Duration.seconds(0)));
        delay.setOnFinished(e -> {
            if (it.hasNext()) {
                current = it.next();
                delay.getKeyFrames().setAll(new KeyFrame(current.getDuration()));
                delay.playFromStart();
                textArea.appendText(current.getContent());
                if (current.requiresInput()) {
                    waitForInput = true;
                    delay.pause();
                }
            }
        });
        delay.playFromStart();

        primaryStage.setScene(scene);
        primaryStage.show();

        scene.addEventFilter(KeyEvent.KEY_PRESSED, e ->
        {
            if (waitForInput && e.getCode().equals(KeyCode.ENTER)) {
                delay.play();
                waitForInput = false;
            }
        });
        scene.addEventFilter(KeyEvent.KEY_TYPED, e -> {
            if (!waitForInput) {
                e.consume();
            }
        });

    }

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

    class Msg {
        private boolean requireInput;
        private String content;
        private Duration duration;

        public Msg(String c, Duration d, boolean b) {
            content = c;
            duration = d;
            requireInput = b;
        }

        public String getContent() {
            return content;
        }

        public Duration getDuration() {
            return duration;
        }

        public boolean requiresInput() {
            return requireInput;
        }
    }
}

【讨论】:

  • 对此的轻微变化可能会更简单一些,即拥有多个关键帧(例如,在 1 秒和 2 秒),并为关键帧附加各种事件处理程序字符串。这样您就不会拥有“嵌套时间线”,其中一个时间线在完成后会创建另一个时间线。
  • 确实,他必须创建一个新帧,删除旧帧并添加新创建的帧,而不是创建新的时间线!
  • 为什么要“删除旧的”?
  • 如果他改变持续时间,假设第一个是 2 秒,新的是 0.5 秒,时间线将需要 2 秒才能完成,
  • 谢谢,这很好用,但是当我想添加很多行时,事情会变得很笨重。
猜你喜欢
  • 2019-10-26
  • 1970-01-01
  • 2019-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-15
  • 2011-10-01
相关资源
最近更新 更多