简体   繁体   English

JavaFX:在TextArea中显示文本,每行之间有延迟

[英]JavaFX: Display text in TextArea with delay in between each line

I'm trying to display text in TextArea with delay in between each sentence, like you're having a conversation. 我正在尝试在TextArea中显示文本,每个句子之间都有延迟,就像您在进行对话一样。 I tried using the sleep function but this doesn't work since the text only gets displayed when all methods stopped running. 我尝试使用sleep函数,但是这不起作用,因为仅在所有方法停止运行时才显示文本。 What would be an efficiënt way to do this: 一种有效的方法是:

(Pseudo code) (伪代码)

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.");

To clarify what I'm trying to do: Display text in textArea with delays inbetween and be able to run functions in between. 为了澄清我要做什么:在textArea中显示文本,中间有延迟,并且能够在它们之间运行函数。

you can use a Timeline's onFinished to make delayed actions in JavaFX 您可以使用时间轴的onFinished在JavaFX中进行延迟的操作

try the following code 试试下面的代码

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;
        }
    }
}

As a variation on the timeline in the other answer, you can create a different KeyFrame for every message you want to display. 作为其他答案在时间轴上的一种变化,您可以为要显示的每条消息创建一个不同的KeyFrame This avoids the scenario of having "nested timelines", which I think would become unmanageable if you had more than two or three messages to display one after the other. 这样可以避免出现“嵌套时间表”的情况,如果您有两个以上的消息要一个接一个地显示,那么我认为这将变得难以管理。

Here's a SSCCE using this idea: 这是使用此想法的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);
    }
}

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

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